Compare commits
39 Commits
57c4309cf2
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| c71272ff13 | |||
| 050027fcb0 | |||
| f9007403d9 | |||
| 9ff1d10d00 | |||
| b8b9f6aa82 | |||
| 0916385521 | |||
| 0afd478761 | |||
| 7530303389 | |||
| 4e41048352 | |||
| e40b1f120b | |||
| 2d5875d211 | |||
| 86ed9346aa | |||
| b10aacaee7 | |||
| d4e93b047c | |||
| fb66a2d09d | |||
| 57bc80f4f9 | |||
| 4597d0a4aa | |||
| d1372b9b59 | |||
| a93910a43d | |||
| ad4a1ac6f2 | |||
| 206a1a288d | |||
| c813b02b90 | |||
| dfce12ee10 | |||
| 2062c35fde | |||
| 160bf65a1f | |||
| f742dcfaff | |||
| 0f557eb010 | |||
| 58f2190199 | |||
| 13e8c538c7 | |||
| bf755a6d02 | |||
| 35ecd108ab | |||
| ce9ea1fd52 | |||
| 4380705f81 | |||
| 09131d7fab | |||
| e0411333fb | |||
| 7191fb19f0 | |||
| 8afe023901 | |||
| 96bad30f15 | |||
| 1f9d5426b8 |
16
.gitignore
vendored
@ -28,7 +28,6 @@
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
@ -47,11 +46,14 @@
|
||||
*.vsix
|
||||
|
||||
.cache/
|
||||
build/
|
||||
build-windows/
|
||||
build-linux/
|
||||
build-ios/
|
||||
build-mac/
|
||||
build-android/
|
||||
./build
|
||||
./build-windows/
|
||||
./build-linux/
|
||||
./build-ios/
|
||||
./build-mac/
|
||||
./build-android-x64/
|
||||
./build-android-armv8/
|
||||
|
||||
imgui.ini
|
||||
|
||||
Playground/
|
||||
|
||||
9
.gitmodules
vendored
@ -1,6 +1,3 @@
|
||||
[submodule "Vendor/SDL"]
|
||||
path = Vendor/SDL
|
||||
url = https://github.com/libsdl-org/SDL
|
||||
[submodule "Vendor/SDL_mixer"]
|
||||
path = Vendor/SDL_mixer
|
||||
url = https://github.com/libsdl-org/SDL_mixer
|
||||
@ -31,3 +28,9 @@
|
||||
[submodule "Vendor/json"]
|
||||
path = Vendor/json
|
||||
url = https://github.com/nlohmann/json
|
||||
[submodule "Vendor/SDL"]
|
||||
path = Vendor/SDL
|
||||
url = https://github.com/I-A-S/SDL
|
||||
[submodule "Vendor/lunasvg"]
|
||||
path = Vendor/lunasvg
|
||||
url = https://github.com/sammycage/lunasvg
|
||||
|
||||
4
.vscode/launch.json
vendored
@ -8,10 +8,10 @@
|
||||
"name": "(Windows) Launch",
|
||||
"type": "cppvsdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/bin/Debug/IAERuntime.exe",
|
||||
"program": "${workspaceFolder}/build/bin/Debug/RPGSample.exe",
|
||||
"args": [],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}/Samples/SpaceInvaders",
|
||||
"cwd": "${workspaceFolder}/Samples/RPG",
|
||||
"environment": [],
|
||||
"console": "externalTerminal",
|
||||
"preLaunchTask": "CMake: build"
|
||||
|
||||
72
.vscode/settings.json
vendored
@ -21,6 +21,76 @@
|
||||
"array": "cpp",
|
||||
"ranges": "cpp",
|
||||
"span": "cpp",
|
||||
"vector": "cpp"
|
||||
"vector": "cpp",
|
||||
"xiosbase": "cpp",
|
||||
"thread": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"atomic": "cpp",
|
||||
"bit": "cpp",
|
||||
"bitset": "cpp",
|
||||
"cctype": "cpp",
|
||||
"charconv": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"compare": "cpp",
|
||||
"concepts": "cpp",
|
||||
"condition_variable": "cpp",
|
||||
"coroutine": "cpp",
|
||||
"csignal": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"deque": "cpp",
|
||||
"exception": "cpp",
|
||||
"forward_list": "cpp",
|
||||
"fstream": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"ios": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"iostream": "cpp",
|
||||
"istream": "cpp",
|
||||
"iterator": "cpp",
|
||||
"limits": "cpp",
|
||||
"list": "cpp",
|
||||
"locale": "cpp",
|
||||
"map": "cpp",
|
||||
"memory": "cpp",
|
||||
"mutex": "cpp",
|
||||
"new": "cpp",
|
||||
"numeric": "cpp",
|
||||
"ostream": "cpp",
|
||||
"queue": "cpp",
|
||||
"ratio": "cpp",
|
||||
"set": "cpp",
|
||||
"shared_mutex": "cpp",
|
||||
"source_location": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stack": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"stop_token": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"string": "cpp",
|
||||
"tuple": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"unordered_set": "cpp",
|
||||
"utility": "cpp",
|
||||
"valarray": "cpp",
|
||||
"variant": "cpp",
|
||||
"xfacet": "cpp",
|
||||
"xhash": "cpp",
|
||||
"xlocbuf": "cpp",
|
||||
"xlocmes": "cpp",
|
||||
"xloctime": "cpp",
|
||||
"xmemory": "cpp",
|
||||
"xstddef": "cpp",
|
||||
"xtree": "cpp",
|
||||
"expected": "cpp"
|
||||
}
|
||||
}
|
||||
15
Android/AndroidProject/.gitignore
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
3
Android/AndroidProject/.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
1
Android/AndroidProject/.idea/.name
generated
Normal file
@ -0,0 +1 @@
|
||||
IAEGame
|
||||
6
Android/AndroidProject/.idea/AndroidProjectSystem.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AndroidProjectSystem">
|
||||
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
|
||||
</component>
|
||||
</project>
|
||||
6
Android/AndroidProject/.idea/compiler.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="21" />
|
||||
</component>
|
||||
</project>
|
||||
10
Android/AndroidProject/.idea/deploymentTargetSelector.xml
generated
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetSelector">
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
</project>
|
||||
19
Android/AndroidProject/.idea/gradle.xml
generated
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="CHOOSE_PER_TEST" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
61
Android/AndroidProject/.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@ -0,0 +1,61 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="ComposePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ComposePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ComposePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ComposePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="GlancePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="GlancePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="GlancePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="GlancePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewDeviceShouldUseNewSpec" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewParameterProviderOnFirstParameter" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
10
Android/AndroidProject/.idea/migrations.xml
generated
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectMigrations">
|
||||
<option name="MigrateToGradleLocalJavaHome">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
10
Android/AndroidProject/.idea/misc.xml
generated
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
||||
17
Android/AndroidProject/.idea/runConfigurations.xml
generated
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
14
Android/AndroidProject/.idea/vcs.xml
generated
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../Vendor/IACore" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../Vendor/RmlUi" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../Vendor/SDL" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../Vendor/SDL_mixer" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../Vendor/freetype" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../Vendor/json" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../Vendor/nativefiledialog" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../Vendor/zlib" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
1
Android/AndroidProject/app/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build
|
||||
59
Android/AndroidProject/app/build.gradle.kts
Normal file
@ -0,0 +1,59 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.kotlin.compose)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.iasoft.iaegame"
|
||||
compileSdk = 36
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.iasoft.iaegame"
|
||||
minSdk = 30
|
||||
targetSdk = 36
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.compose.ui)
|
||||
implementation(libs.androidx.compose.ui.graphics)
|
||||
implementation(libs.androidx.compose.ui.tooling.preview)
|
||||
implementation(libs.androidx.compose.material3)
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
|
||||
debugImplementation(libs.androidx.compose.ui.tooling)
|
||||
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
||||
}
|
||||
21
Android/AndroidProject/app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
57
Android/AndroidProject/app/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<!-- OpenGL ES 2.0 -->
|
||||
<uses-feature android:glEsVersion="0x00020000" />
|
||||
|
||||
<!-- Touchscreen support -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.touchscreen"
|
||||
android:required="false" />
|
||||
|
||||
<!-- Game controller support -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.bluetooth"
|
||||
android:required="false" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.gamepad"
|
||||
android:required="false" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.usb.host"
|
||||
android:required="false" />
|
||||
|
||||
<!-- External mouse input events -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.type.pc"
|
||||
android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.IAEGame"
|
||||
android:hardwareAccelerated="true">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.IAEGame"
|
||||
android:configChanges="layoutDirection|locale|grammaticalGender|fontScale|fontWeightAdjustment|orientation|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"
|
||||
android:preferMinimalPostProcessing="true"
|
||||
>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
BIN
Android/AndroidProject/app/src/main/ic_launcher-playstore.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
@ -0,0 +1,16 @@
|
||||
package com.iasoft.iaegame
|
||||
|
||||
import android.os.Bundle
|
||||
import org.libsdl.app.SDLActivity
|
||||
|
||||
class MainActivity : SDLActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
override fun getLibraries(): Array<out String?>? {
|
||||
return arrayOf<String>(
|
||||
"Game"
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package com.iasoft.iaegame.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val Purple80 = Color(0xFFD0BCFF)
|
||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
val Pink80 = Color(0xFFEFB8C8)
|
||||
|
||||
val Purple40 = Color(0xFF6650a4)
|
||||
val PurpleGrey40 = Color(0xFF625b71)
|
||||
val Pink40 = Color(0xFF7D5260)
|
||||
@ -0,0 +1,58 @@
|
||||
package com.iasoft.iaegame.ui.theme
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = Purple80,
|
||||
secondary = PurpleGrey80,
|
||||
tertiary = Pink80
|
||||
)
|
||||
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = Purple40,
|
||||
secondary = PurpleGrey40,
|
||||
tertiary = Pink40
|
||||
|
||||
/* Other default colors to override
|
||||
background = Color(0xFFFFFBFE),
|
||||
surface = Color(0xFFFFFBFE),
|
||||
onPrimary = Color.White,
|
||||
onSecondary = Color.White,
|
||||
onTertiary = Color.White,
|
||||
onBackground = Color(0xFF1C1B1F),
|
||||
onSurface = Color(0xFF1C1B1F),
|
||||
*/
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun IAEGameTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = true,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
}
|
||||
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package com.iasoft.iaegame.ui.theme
|
||||
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
// Set of Material typography styles to start with
|
||||
val Typography = Typography(
|
||||
bodyLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 16.sp,
|
||||
lineHeight = 24.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
/* Other default text styles to override
|
||||
titleLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 22.sp,
|
||||
lineHeight = 28.sp,
|
||||
letterSpacing = 0.sp
|
||||
),
|
||||
labelSmall = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 11.sp,
|
||||
lineHeight = 16.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
*/
|
||||
)
|
||||
@ -0,0 +1,21 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
import android.hardware.usb.UsbDevice;
|
||||
|
||||
interface HIDDevice
|
||||
{
|
||||
public int getId();
|
||||
public int getVendorId();
|
||||
public int getProductId();
|
||||
public String getSerialNumber();
|
||||
public int getVersion();
|
||||
public String getManufacturerName();
|
||||
public String getProductName();
|
||||
public UsbDevice getDevice();
|
||||
public boolean open();
|
||||
public int writeReport(byte[] report, boolean feature);
|
||||
public boolean readReport(byte[] report, boolean feature);
|
||||
public void setFrozen(boolean frozen);
|
||||
public void close();
|
||||
public void shutdown();
|
||||
}
|
||||
@ -0,0 +1,655 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
import android.content.Context;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCallback;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattDescriptor;
|
||||
import android.bluetooth.BluetoothManager;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.bluetooth.BluetoothGattService;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.os.*;
|
||||
|
||||
//import com.android.internal.util.HexDump;
|
||||
|
||||
import java.lang.Runnable;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.UUID;
|
||||
|
||||
class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDevice {
|
||||
|
||||
private static final String TAG = "hidapi";
|
||||
private HIDDeviceManager mManager;
|
||||
private BluetoothDevice mDevice;
|
||||
private int mDeviceId;
|
||||
private BluetoothGatt mGatt;
|
||||
private boolean mIsRegistered = false;
|
||||
private boolean mIsConnected = false;
|
||||
private boolean mIsChromebook = false;
|
||||
private boolean mIsReconnecting = false;
|
||||
private boolean mFrozen = false;
|
||||
private LinkedList<GattOperation> mOperations;
|
||||
GattOperation mCurrentOperation = null;
|
||||
private Handler mHandler;
|
||||
|
||||
private static final int TRANSPORT_AUTO = 0;
|
||||
private static final int TRANSPORT_BREDR = 1;
|
||||
private static final int TRANSPORT_LE = 2;
|
||||
|
||||
private static final int CHROMEBOOK_CONNECTION_CHECK_INTERVAL = 10000;
|
||||
|
||||
static final UUID steamControllerService = UUID.fromString("100F6C32-1735-4313-B402-38567131E5F3");
|
||||
static final UUID inputCharacteristic = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3");
|
||||
static final UUID reportCharacteristic = UUID.fromString("100F6C34-1735-4313-B402-38567131E5F3");
|
||||
static private final byte[] enterValveMode = new byte[] { (byte)0xC0, (byte)0x87, 0x03, 0x08, 0x07, 0x00 };
|
||||
|
||||
static class GattOperation {
|
||||
private enum Operation {
|
||||
CHR_READ,
|
||||
CHR_WRITE,
|
||||
ENABLE_NOTIFICATION
|
||||
}
|
||||
|
||||
Operation mOp;
|
||||
UUID mUuid;
|
||||
byte[] mValue;
|
||||
BluetoothGatt mGatt;
|
||||
boolean mResult = true;
|
||||
|
||||
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid) {
|
||||
mGatt = gatt;
|
||||
mOp = operation;
|
||||
mUuid = uuid;
|
||||
}
|
||||
|
||||
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value) {
|
||||
mGatt = gatt;
|
||||
mOp = operation;
|
||||
mUuid = uuid;
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
// This is executed in main thread
|
||||
BluetoothGattCharacteristic chr;
|
||||
|
||||
switch (mOp) {
|
||||
case CHR_READ:
|
||||
chr = getCharacteristic(mUuid);
|
||||
//Log.v(TAG, "Reading characteristic " + chr.getUuid());
|
||||
if (!mGatt.readCharacteristic(chr)) {
|
||||
Log.e(TAG, "Unable to read characteristic " + mUuid.toString());
|
||||
mResult = false;
|
||||
break;
|
||||
}
|
||||
mResult = true;
|
||||
break;
|
||||
case CHR_WRITE:
|
||||
chr = getCharacteristic(mUuid);
|
||||
//Log.v(TAG, "Writing characteristic " + chr.getUuid() + " value=" + HexDump.toHexString(value));
|
||||
chr.setValue(mValue);
|
||||
if (!mGatt.writeCharacteristic(chr)) {
|
||||
Log.e(TAG, "Unable to write characteristic " + mUuid.toString());
|
||||
mResult = false;
|
||||
break;
|
||||
}
|
||||
mResult = true;
|
||||
break;
|
||||
case ENABLE_NOTIFICATION:
|
||||
chr = getCharacteristic(mUuid);
|
||||
//Log.v(TAG, "Writing descriptor of " + chr.getUuid());
|
||||
if (chr != null) {
|
||||
BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
|
||||
if (cccd != null) {
|
||||
int properties = chr.getProperties();
|
||||
byte[] value;
|
||||
if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == BluetoothGattCharacteristic.PROPERTY_NOTIFY) {
|
||||
value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
|
||||
} else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) == BluetoothGattCharacteristic.PROPERTY_INDICATE) {
|
||||
value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;
|
||||
} else {
|
||||
Log.e(TAG, "Unable to start notifications on input characteristic");
|
||||
mResult = false;
|
||||
return;
|
||||
}
|
||||
|
||||
mGatt.setCharacteristicNotification(chr, true);
|
||||
cccd.setValue(value);
|
||||
if (!mGatt.writeDescriptor(cccd)) {
|
||||
Log.e(TAG, "Unable to write descriptor " + mUuid.toString());
|
||||
mResult = false;
|
||||
return;
|
||||
}
|
||||
mResult = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean finish() {
|
||||
return mResult;
|
||||
}
|
||||
|
||||
private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
|
||||
BluetoothGattService valveService = mGatt.getService(steamControllerService);
|
||||
if (valveService == null)
|
||||
return null;
|
||||
return valveService.getCharacteristic(uuid);
|
||||
}
|
||||
|
||||
static public GattOperation readCharacteristic(BluetoothGatt gatt, UUID uuid) {
|
||||
return new GattOperation(gatt, Operation.CHR_READ, uuid);
|
||||
}
|
||||
|
||||
static public GattOperation writeCharacteristic(BluetoothGatt gatt, UUID uuid, byte[] value) {
|
||||
return new GattOperation(gatt, Operation.CHR_WRITE, uuid, value);
|
||||
}
|
||||
|
||||
static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid) {
|
||||
return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid);
|
||||
}
|
||||
}
|
||||
|
||||
HIDDeviceBLESteamController(HIDDeviceManager manager, BluetoothDevice device) {
|
||||
mManager = manager;
|
||||
mDevice = device;
|
||||
mDeviceId = mManager.getDeviceIDForIdentifier(getIdentifier());
|
||||
mIsRegistered = false;
|
||||
mIsChromebook = SDLActivity.isChromebook();
|
||||
mOperations = new LinkedList<GattOperation>();
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
mGatt = connectGatt();
|
||||
// final HIDDeviceBLESteamController finalThis = this;
|
||||
// mHandler.postDelayed(new Runnable() {
|
||||
// @Override
|
||||
// void run() {
|
||||
// finalThis.checkConnectionForChromebookIssue();
|
||||
// }
|
||||
// }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
|
||||
}
|
||||
|
||||
String getIdentifier() {
|
||||
return String.format("SteamController.%s", mDevice.getAddress());
|
||||
}
|
||||
|
||||
BluetoothGatt getGatt() {
|
||||
return mGatt;
|
||||
}
|
||||
|
||||
// Because on Chromebooks we show up as a dual-mode device, it will attempt to connect TRANSPORT_AUTO, which will use TRANSPORT_BREDR instead
|
||||
// of TRANSPORT_LE. Let's force ourselves to connect low energy.
|
||||
private BluetoothGatt connectGatt(boolean managed) {
|
||||
if (Build.VERSION.SDK_INT >= 23 /* Android 6.0 (M) */) {
|
||||
try {
|
||||
return mDevice.connectGatt(mManager.getContext(), managed, this, TRANSPORT_LE);
|
||||
} catch (Exception e) {
|
||||
return mDevice.connectGatt(mManager.getContext(), managed, this);
|
||||
}
|
||||
} else {
|
||||
return mDevice.connectGatt(mManager.getContext(), managed, this);
|
||||
}
|
||||
}
|
||||
|
||||
private BluetoothGatt connectGatt() {
|
||||
return connectGatt(false);
|
||||
}
|
||||
|
||||
protected int getConnectionState() {
|
||||
|
||||
Context context = mManager.getContext();
|
||||
if (context == null) {
|
||||
// We are lacking any context to get our Bluetooth information. We'll just assume disconnected.
|
||||
return BluetoothProfile.STATE_DISCONNECTED;
|
||||
}
|
||||
|
||||
BluetoothManager btManager = (BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE);
|
||||
if (btManager == null) {
|
||||
// This device doesn't support Bluetooth. We should never be here, because how did
|
||||
// we instantiate a device to start with?
|
||||
return BluetoothProfile.STATE_DISCONNECTED;
|
||||
}
|
||||
|
||||
return btManager.getConnectionState(mDevice, BluetoothProfile.GATT);
|
||||
}
|
||||
|
||||
void reconnect() {
|
||||
|
||||
if (getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
|
||||
mGatt.disconnect();
|
||||
mGatt = connectGatt();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void checkConnectionForChromebookIssue() {
|
||||
if (!mIsChromebook) {
|
||||
// We only do this on Chromebooks, because otherwise it's really annoying to just attempt
|
||||
// over and over.
|
||||
return;
|
||||
}
|
||||
|
||||
int connectionState = getConnectionState();
|
||||
|
||||
switch (connectionState) {
|
||||
case BluetoothProfile.STATE_CONNECTED:
|
||||
if (!mIsConnected) {
|
||||
// We are in the Bad Chromebook Place. We can force a disconnect
|
||||
// to try to recover.
|
||||
Log.v(TAG, "Chromebook: We are in a very bad state; the controller shows as connected in the underlying Bluetooth layer, but we never received a callback. Forcing a reconnect.");
|
||||
mIsReconnecting = true;
|
||||
mGatt.disconnect();
|
||||
mGatt = connectGatt(false);
|
||||
break;
|
||||
}
|
||||
else if (!isRegistered()) {
|
||||
if (mGatt.getServices().size() > 0) {
|
||||
Log.v(TAG, "Chromebook: We are connected to a controller, but never got our registration. Trying to recover.");
|
||||
probeService(this);
|
||||
}
|
||||
else {
|
||||
Log.v(TAG, "Chromebook: We are connected to a controller, but never discovered services. Trying to recover.");
|
||||
mIsReconnecting = true;
|
||||
mGatt.disconnect();
|
||||
mGatt = connectGatt(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Log.v(TAG, "Chromebook: We are connected, and registered. Everything's good!");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case BluetoothProfile.STATE_DISCONNECTED:
|
||||
Log.v(TAG, "Chromebook: We have either been disconnected, or the Chromebook BtGatt.ContextMap bug has bitten us. Attempting a disconnect/reconnect, but we may not be able to recover.");
|
||||
|
||||
mIsReconnecting = true;
|
||||
mGatt.disconnect();
|
||||
mGatt = connectGatt(false);
|
||||
break;
|
||||
|
||||
case BluetoothProfile.STATE_CONNECTING:
|
||||
Log.v(TAG, "Chromebook: We're still trying to connect. Waiting a bit longer.");
|
||||
break;
|
||||
}
|
||||
|
||||
final HIDDeviceBLESteamController finalThis = this;
|
||||
mHandler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
finalThis.checkConnectionForChromebookIssue();
|
||||
}
|
||||
}, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
|
||||
}
|
||||
|
||||
private boolean isRegistered() {
|
||||
return mIsRegistered;
|
||||
}
|
||||
|
||||
private void setRegistered() {
|
||||
mIsRegistered = true;
|
||||
}
|
||||
|
||||
private boolean probeService(HIDDeviceBLESteamController controller) {
|
||||
|
||||
if (isRegistered()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!mIsConnected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Log.v(TAG, "probeService controller=" + controller);
|
||||
|
||||
for (BluetoothGattService service : mGatt.getServices()) {
|
||||
if (service.getUuid().equals(steamControllerService)) {
|
||||
Log.v(TAG, "Found Valve steam controller service " + service.getUuid());
|
||||
|
||||
for (BluetoothGattCharacteristic chr : service.getCharacteristics()) {
|
||||
if (chr.getUuid().equals(inputCharacteristic)) {
|
||||
Log.v(TAG, "Found input characteristic");
|
||||
// Start notifications
|
||||
BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
|
||||
if (cccd != null) {
|
||||
enableNotification(chr.getUuid());
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((mGatt.getServices().size() == 0) && mIsChromebook && !mIsReconnecting) {
|
||||
Log.e(TAG, "Chromebook: Discovered services were empty; this almost certainly means the BtGatt.ContextMap bug has bitten us.");
|
||||
mIsConnected = false;
|
||||
mIsReconnecting = true;
|
||||
mGatt.disconnect();
|
||||
mGatt = connectGatt(false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void finishCurrentGattOperation() {
|
||||
GattOperation op = null;
|
||||
synchronized (mOperations) {
|
||||
if (mCurrentOperation != null) {
|
||||
op = mCurrentOperation;
|
||||
mCurrentOperation = null;
|
||||
}
|
||||
}
|
||||
if (op != null) {
|
||||
boolean result = op.finish(); // TODO: Maybe in main thread as well?
|
||||
|
||||
// Our operation failed, let's add it back to the beginning of our queue.
|
||||
if (!result) {
|
||||
mOperations.addFirst(op);
|
||||
}
|
||||
}
|
||||
executeNextGattOperation();
|
||||
}
|
||||
|
||||
private void executeNextGattOperation() {
|
||||
synchronized (mOperations) {
|
||||
if (mCurrentOperation != null)
|
||||
return;
|
||||
|
||||
if (mOperations.isEmpty())
|
||||
return;
|
||||
|
||||
mCurrentOperation = mOperations.removeFirst();
|
||||
}
|
||||
|
||||
// Run in main thread
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (mOperations) {
|
||||
if (mCurrentOperation == null) {
|
||||
Log.e(TAG, "Current operation null in executor?");
|
||||
return;
|
||||
}
|
||||
|
||||
mCurrentOperation.run();
|
||||
// now wait for the GATT callback and when it comes, finish this operation
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void queueGattOperation(GattOperation op) {
|
||||
synchronized (mOperations) {
|
||||
mOperations.add(op);
|
||||
}
|
||||
executeNextGattOperation();
|
||||
}
|
||||
|
||||
private void enableNotification(UUID chrUuid) {
|
||||
GattOperation op = HIDDeviceBLESteamController.GattOperation.enableNotification(mGatt, chrUuid);
|
||||
queueGattOperation(op);
|
||||
}
|
||||
|
||||
void writeCharacteristic(UUID uuid, byte[] value) {
|
||||
GattOperation op = HIDDeviceBLESteamController.GattOperation.writeCharacteristic(mGatt, uuid, value);
|
||||
queueGattOperation(op);
|
||||
}
|
||||
|
||||
void readCharacteristic(UUID uuid) {
|
||||
GattOperation op = HIDDeviceBLESteamController.GattOperation.readCharacteristic(mGatt, uuid);
|
||||
queueGattOperation(op);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////// BluetoothGattCallback overridden methods
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void onConnectionStateChange(BluetoothGatt g, int status, int newState) {
|
||||
//Log.v(TAG, "onConnectionStateChange status=" + status + " newState=" + newState);
|
||||
mIsReconnecting = false;
|
||||
if (newState == 2) {
|
||||
mIsConnected = true;
|
||||
// Run directly, without GattOperation
|
||||
if (!isRegistered()) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mGatt.discoverServices();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (newState == 0) {
|
||||
mIsConnected = false;
|
||||
}
|
||||
|
||||
// Disconnection is handled in SteamLink using the ACTION_ACL_DISCONNECTED Intent.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
|
||||
//Log.v(TAG, "onServicesDiscovered status=" + status);
|
||||
if (status == 0) {
|
||||
if (gatt.getServices().size() == 0) {
|
||||
Log.v(TAG, "onServicesDiscovered returned zero services; something has gone horribly wrong down in Android's Bluetooth stack.");
|
||||
mIsReconnecting = true;
|
||||
mIsConnected = false;
|
||||
gatt.disconnect();
|
||||
mGatt = connectGatt(false);
|
||||
}
|
||||
else {
|
||||
probeService(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
|
||||
//Log.v(TAG, "onCharacteristicRead status=" + status + " uuid=" + characteristic.getUuid());
|
||||
|
||||
if (characteristic.getUuid().equals(reportCharacteristic) && !mFrozen) {
|
||||
mManager.HIDDeviceReportResponse(getId(), characteristic.getValue());
|
||||
}
|
||||
|
||||
finishCurrentGattOperation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
|
||||
//Log.v(TAG, "onCharacteristicWrite status=" + status + " uuid=" + characteristic.getUuid());
|
||||
|
||||
if (characteristic.getUuid().equals(reportCharacteristic)) {
|
||||
// Only register controller with the native side once it has been fully configured
|
||||
if (!isRegistered()) {
|
||||
Log.v(TAG, "Registering Steam Controller with ID: " + getId());
|
||||
mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0, 0, 0, 0, true);
|
||||
setRegistered();
|
||||
}
|
||||
}
|
||||
|
||||
finishCurrentGattOperation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
|
||||
// Enable this for verbose logging of controller input reports
|
||||
//Log.v(TAG, "onCharacteristicChanged uuid=" + characteristic.getUuid() + " data=" + HexDump.dumpHexString(characteristic.getValue()));
|
||||
|
||||
if (characteristic.getUuid().equals(inputCharacteristic) && !mFrozen) {
|
||||
mManager.HIDDeviceInputReport(getId(), characteristic.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
|
||||
//Log.v(TAG, "onDescriptorRead status=" + status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
|
||||
BluetoothGattCharacteristic chr = descriptor.getCharacteristic();
|
||||
//Log.v(TAG, "onDescriptorWrite status=" + status + " uuid=" + chr.getUuid() + " descriptor=" + descriptor.getUuid());
|
||||
|
||||
if (chr.getUuid().equals(inputCharacteristic)) {
|
||||
boolean hasWrittenInputDescriptor = true;
|
||||
BluetoothGattCharacteristic reportChr = chr.getService().getCharacteristic(reportCharacteristic);
|
||||
if (reportChr != null) {
|
||||
Log.v(TAG, "Writing report characteristic to enter valve mode");
|
||||
reportChr.setValue(enterValveMode);
|
||||
gatt.writeCharacteristic(reportChr);
|
||||
}
|
||||
}
|
||||
|
||||
finishCurrentGattOperation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
|
||||
//Log.v(TAG, "onReliableWriteCompleted status=" + status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
|
||||
//Log.v(TAG, "onReadRemoteRssi status=" + status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
|
||||
//Log.v(TAG, "onMtuChanged status=" + status);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////// Public API
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return mDeviceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVendorId() {
|
||||
// Valve Corporation
|
||||
final int VALVE_USB_VID = 0x28DE;
|
||||
return VALVE_USB_VID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProductId() {
|
||||
// We don't have an easy way to query from the Bluetooth device, but we know what it is
|
||||
final int D0G_BLE2_PID = 0x1106;
|
||||
return D0G_BLE2_PID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSerialNumber() {
|
||||
// This will be read later via feature report by Steam
|
||||
return "12345";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getManufacturerName() {
|
||||
return "Valve Corporation";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProductName() {
|
||||
return "Steam Controller";
|
||||
}
|
||||
|
||||
@Override
|
||||
public UsbDevice getDevice() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean open() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeReport(byte[] report, boolean feature) {
|
||||
if (!isRegistered()) {
|
||||
Log.e(TAG, "Attempted writeReport before Steam Controller is registered!");
|
||||
if (mIsConnected) {
|
||||
probeService(this);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (feature) {
|
||||
// We need to skip the first byte, as that doesn't go over the air
|
||||
byte[] actual_report = Arrays.copyOfRange(report, 1, report.length - 1);
|
||||
//Log.v(TAG, "writeFeatureReport " + HexDump.dumpHexString(actual_report));
|
||||
writeCharacteristic(reportCharacteristic, actual_report);
|
||||
return report.length;
|
||||
} else {
|
||||
//Log.v(TAG, "writeOutputReport " + HexDump.dumpHexString(report));
|
||||
writeCharacteristic(reportCharacteristic, report);
|
||||
return report.length;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readReport(byte[] report, boolean feature) {
|
||||
if (!isRegistered()) {
|
||||
Log.e(TAG, "Attempted readReport before Steam Controller is registered!");
|
||||
if (mIsConnected) {
|
||||
probeService(this);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (feature) {
|
||||
readCharacteristic(reportCharacteristic);
|
||||
return true;
|
||||
} else {
|
||||
// Not implemented
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFrozen(boolean frozen) {
|
||||
mFrozen = frozen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
close();
|
||||
|
||||
BluetoothGatt g = mGatt;
|
||||
if (g != null) {
|
||||
g.disconnect();
|
||||
g.close();
|
||||
mGatt = null;
|
||||
}
|
||||
mManager = null;
|
||||
mIsRegistered = false;
|
||||
mIsConnected = false;
|
||||
mOperations.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,690 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.PendingIntent;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothManager;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.hardware.usb.*;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class HIDDeviceManager {
|
||||
private static final String TAG = "hidapi";
|
||||
private static final String ACTION_USB_PERMISSION = "org.libsdl.app.USB_PERMISSION";
|
||||
|
||||
private static HIDDeviceManager sManager;
|
||||
private static int sManagerRefCount = 0;
|
||||
|
||||
static public HIDDeviceManager acquire(Context context) {
|
||||
if (sManagerRefCount == 0) {
|
||||
sManager = new HIDDeviceManager(context);
|
||||
}
|
||||
++sManagerRefCount;
|
||||
return sManager;
|
||||
}
|
||||
|
||||
static public void release(HIDDeviceManager manager) {
|
||||
if (manager == sManager) {
|
||||
--sManagerRefCount;
|
||||
if (sManagerRefCount == 0) {
|
||||
sManager.close();
|
||||
sManager = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Context mContext;
|
||||
private HashMap<Integer, HIDDevice> mDevicesById = new HashMap<Integer, HIDDevice>();
|
||||
private HashMap<BluetoothDevice, HIDDeviceBLESteamController> mBluetoothDevices = new HashMap<BluetoothDevice, HIDDeviceBLESteamController>();
|
||||
private int mNextDeviceId = 0;
|
||||
private SharedPreferences mSharedPreferences = null;
|
||||
private boolean mIsChromebook = false;
|
||||
private UsbManager mUsbManager;
|
||||
private Handler mHandler;
|
||||
private BluetoothManager mBluetoothManager;
|
||||
private List<BluetoothDevice> mLastBluetoothDevices;
|
||||
|
||||
private final BroadcastReceiver mUsbBroadcast = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
|
||||
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||
handleUsbDeviceAttached(usbDevice);
|
||||
} else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
|
||||
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||
handleUsbDeviceDetached(usbDevice);
|
||||
} else if (action.equals(HIDDeviceManager.ACTION_USB_PERMISSION)) {
|
||||
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||
handleUsbDevicePermission(usbDevice, intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final BroadcastReceiver mBluetoothBroadcast = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
// Bluetooth device was connected. If it was a Steam Controller, handle it
|
||||
if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
|
||||
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||
Log.d(TAG, "Bluetooth device connected: " + device);
|
||||
|
||||
if (isSteamController(device)) {
|
||||
connectBluetoothDevice(device);
|
||||
}
|
||||
}
|
||||
|
||||
// Bluetooth device was disconnected, remove from controller manager (if any)
|
||||
if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
|
||||
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||
Log.d(TAG, "Bluetooth device disconnected: " + device);
|
||||
|
||||
disconnectBluetoothDevice(device);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private HIDDeviceManager(final Context context) {
|
||||
mContext = context;
|
||||
|
||||
HIDDeviceRegisterCallback();
|
||||
|
||||
mSharedPreferences = mContext.getSharedPreferences("hidapi", Context.MODE_PRIVATE);
|
||||
mIsChromebook = SDLActivity.isChromebook();
|
||||
|
||||
// if (shouldClear) {
|
||||
// SharedPreferences.Editor spedit = mSharedPreferences.edit();
|
||||
// spedit.clear();
|
||||
// spedit.apply();
|
||||
// }
|
||||
// else
|
||||
{
|
||||
mNextDeviceId = mSharedPreferences.getInt("next_device_id", 0);
|
||||
}
|
||||
}
|
||||
|
||||
Context getContext() {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
int getDeviceIDForIdentifier(String identifier) {
|
||||
SharedPreferences.Editor spedit = mSharedPreferences.edit();
|
||||
|
||||
int result = mSharedPreferences.getInt(identifier, 0);
|
||||
if (result == 0) {
|
||||
result = mNextDeviceId++;
|
||||
spedit.putInt("next_device_id", mNextDeviceId);
|
||||
}
|
||||
|
||||
spedit.putInt(identifier, result);
|
||||
spedit.apply();
|
||||
return result;
|
||||
}
|
||||
|
||||
private void initializeUSB() {
|
||||
mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE);
|
||||
if (mUsbManager == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
// Logging
|
||||
for (UsbDevice device : mUsbManager.getDeviceList().values()) {
|
||||
Log.i(TAG,"Path: " + device.getDeviceName());
|
||||
Log.i(TAG,"Manufacturer: " + device.getManufacturerName());
|
||||
Log.i(TAG,"Product: " + device.getProductName());
|
||||
Log.i(TAG,"ID: " + device.getDeviceId());
|
||||
Log.i(TAG,"Class: " + device.getDeviceClass());
|
||||
Log.i(TAG,"Protocol: " + device.getDeviceProtocol());
|
||||
Log.i(TAG,"Vendor ID " + device.getVendorId());
|
||||
Log.i(TAG,"Product ID: " + device.getProductId());
|
||||
Log.i(TAG,"Interface count: " + device.getInterfaceCount());
|
||||
Log.i(TAG,"---------------------------------------");
|
||||
|
||||
// Get interface details
|
||||
for (int index = 0; index < device.getInterfaceCount(); index++) {
|
||||
UsbInterface mUsbInterface = device.getInterface(index);
|
||||
Log.i(TAG," ***** *****");
|
||||
Log.i(TAG," Interface index: " + index);
|
||||
Log.i(TAG," Interface ID: " + mUsbInterface.getId());
|
||||
Log.i(TAG," Interface class: " + mUsbInterface.getInterfaceClass());
|
||||
Log.i(TAG," Interface subclass: " + mUsbInterface.getInterfaceSubclass());
|
||||
Log.i(TAG," Interface protocol: " + mUsbInterface.getInterfaceProtocol());
|
||||
Log.i(TAG," Endpoint count: " + mUsbInterface.getEndpointCount());
|
||||
|
||||
// Get endpoint details
|
||||
for (int epi = 0; epi < mUsbInterface.getEndpointCount(); epi++)
|
||||
{
|
||||
UsbEndpoint mEndpoint = mUsbInterface.getEndpoint(epi);
|
||||
Log.i(TAG," ++++ ++++ ++++");
|
||||
Log.i(TAG," Endpoint index: " + epi);
|
||||
Log.i(TAG," Attributes: " + mEndpoint.getAttributes());
|
||||
Log.i(TAG," Direction: " + mEndpoint.getDirection());
|
||||
Log.i(TAG," Number: " + mEndpoint.getEndpointNumber());
|
||||
Log.i(TAG," Interval: " + mEndpoint.getInterval());
|
||||
Log.i(TAG," Packet size: " + mEndpoint.getMaxPacketSize());
|
||||
Log.i(TAG," Type: " + mEndpoint.getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.i(TAG," No more devices connected.");
|
||||
*/
|
||||
|
||||
// Register for USB broadcasts and permission completions
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
|
||||
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
|
||||
filter.addAction(HIDDeviceManager.ACTION_USB_PERMISSION);
|
||||
if (Build.VERSION.SDK_INT >= 33) { /* Android 13.0 (TIRAMISU) */
|
||||
mContext.registerReceiver(mUsbBroadcast, filter, Context.RECEIVER_EXPORTED);
|
||||
} else {
|
||||
mContext.registerReceiver(mUsbBroadcast, filter);
|
||||
}
|
||||
|
||||
for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
|
||||
handleUsbDeviceAttached(usbDevice);
|
||||
}
|
||||
}
|
||||
|
||||
UsbManager getUSBManager() {
|
||||
return mUsbManager;
|
||||
}
|
||||
|
||||
private void shutdownUSB() {
|
||||
try {
|
||||
mContext.unregisterReceiver(mUsbBroadcast);
|
||||
} catch (Exception e) {
|
||||
// We may not have registered, that's okay
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isHIDDeviceInterface(UsbDevice usbDevice, UsbInterface usbInterface) {
|
||||
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {
|
||||
return true;
|
||||
}
|
||||
if (isXbox360Controller(usbDevice, usbInterface) || isXboxOneController(usbDevice, usbInterface)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterface) {
|
||||
final int XB360_IFACE_SUBCLASS = 93;
|
||||
final int XB360_IFACE_PROTOCOL = 1; // Wired
|
||||
final int XB360W_IFACE_PROTOCOL = 129; // Wireless
|
||||
final int[] SUPPORTED_VENDORS = {
|
||||
0x0079, // GPD Win 2
|
||||
0x044f, // Thrustmaster
|
||||
0x045e, // Microsoft
|
||||
0x046d, // Logitech
|
||||
0x056e, // Elecom
|
||||
0x06a3, // Saitek
|
||||
0x0738, // Mad Catz
|
||||
0x07ff, // Mad Catz
|
||||
0x0e6f, // PDP
|
||||
0x0f0d, // Hori
|
||||
0x1038, // SteelSeries
|
||||
0x11c9, // Nacon
|
||||
0x12ab, // Unknown
|
||||
0x1430, // RedOctane
|
||||
0x146b, // BigBen
|
||||
0x1532, // Razer Sabertooth
|
||||
0x15e4, // Numark
|
||||
0x162e, // Joytech
|
||||
0x1689, // Razer Onza
|
||||
0x1949, // Lab126, Inc.
|
||||
0x1bad, // Harmonix
|
||||
0x20d6, // PowerA
|
||||
0x24c6, // PowerA
|
||||
0x2c22, // Qanba
|
||||
0x2dc8, // 8BitDo
|
||||
0x9886, // ASTRO Gaming
|
||||
};
|
||||
|
||||
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
|
||||
usbInterface.getInterfaceSubclass() == XB360_IFACE_SUBCLASS &&
|
||||
(usbInterface.getInterfaceProtocol() == XB360_IFACE_PROTOCOL ||
|
||||
usbInterface.getInterfaceProtocol() == XB360W_IFACE_PROTOCOL)) {
|
||||
int vendor_id = usbDevice.getVendorId();
|
||||
for (int supportedVid : SUPPORTED_VENDORS) {
|
||||
if (vendor_id == supportedVid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterface) {
|
||||
final int XB1_IFACE_SUBCLASS = 71;
|
||||
final int XB1_IFACE_PROTOCOL = 208;
|
||||
final int[] SUPPORTED_VENDORS = {
|
||||
0x03f0, // HP
|
||||
0x044f, // Thrustmaster
|
||||
0x045e, // Microsoft
|
||||
0x0738, // Mad Catz
|
||||
0x0b05, // ASUS
|
||||
0x0e6f, // PDP
|
||||
0x0f0d, // Hori
|
||||
0x10f5, // Turtle Beach
|
||||
0x1532, // Razer Wildcat
|
||||
0x20d6, // PowerA
|
||||
0x24c6, // PowerA
|
||||
0x294b, // Snakebyte
|
||||
0x2dc8, // 8BitDo
|
||||
0x2e24, // Hyperkin
|
||||
0x2e95, // SCUF
|
||||
0x3285, // Nacon
|
||||
0x3537, // GameSir
|
||||
0x366c, // ByoWave
|
||||
};
|
||||
|
||||
if (usbInterface.getId() == 0 &&
|
||||
usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
|
||||
usbInterface.getInterfaceSubclass() == XB1_IFACE_SUBCLASS &&
|
||||
usbInterface.getInterfaceProtocol() == XB1_IFACE_PROTOCOL) {
|
||||
int vendor_id = usbDevice.getVendorId();
|
||||
for (int supportedVid : SUPPORTED_VENDORS) {
|
||||
if (vendor_id == supportedVid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void handleUsbDeviceAttached(UsbDevice usbDevice) {
|
||||
connectHIDDeviceUSB(usbDevice);
|
||||
}
|
||||
|
||||
private void handleUsbDeviceDetached(UsbDevice usbDevice) {
|
||||
List<Integer> devices = new ArrayList<Integer>();
|
||||
for (HIDDevice device : mDevicesById.values()) {
|
||||
if (usbDevice.equals(device.getDevice())) {
|
||||
devices.add(device.getId());
|
||||
}
|
||||
}
|
||||
for (int id : devices) {
|
||||
HIDDevice device = mDevicesById.get(id);
|
||||
mDevicesById.remove(id);
|
||||
device.shutdown();
|
||||
HIDDeviceDisconnected(id);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleUsbDevicePermission(UsbDevice usbDevice, boolean permission_granted) {
|
||||
for (HIDDevice device : mDevicesById.values()) {
|
||||
if (usbDevice.equals(device.getDevice())) {
|
||||
boolean opened = false;
|
||||
if (permission_granted) {
|
||||
opened = device.open();
|
||||
}
|
||||
HIDDeviceOpenResult(device.getId(), opened);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void connectHIDDeviceUSB(UsbDevice usbDevice) {
|
||||
synchronized (this) {
|
||||
int interface_mask = 0;
|
||||
for (int interface_index = 0; interface_index < usbDevice.getInterfaceCount(); interface_index++) {
|
||||
UsbInterface usbInterface = usbDevice.getInterface(interface_index);
|
||||
if (isHIDDeviceInterface(usbDevice, usbInterface)) {
|
||||
// Check to see if we've already added this interface
|
||||
// This happens with the Xbox Series X controller which has a duplicate interface 0, which is inactive
|
||||
int interface_id = usbInterface.getId();
|
||||
if ((interface_mask & (1 << interface_id)) != 0) {
|
||||
continue;
|
||||
}
|
||||
interface_mask |= (1 << interface_id);
|
||||
|
||||
HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_index);
|
||||
int id = device.getId();
|
||||
mDevicesById.put(id, device);
|
||||
HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), usbInterface.getId(), usbInterface.getInterfaceClass(), usbInterface.getInterfaceSubclass(), usbInterface.getInterfaceProtocol(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeBluetooth() {
|
||||
Log.d(TAG, "Initializing Bluetooth");
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 31 /* Android 12 */ &&
|
||||
mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH_CONNECT, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
|
||||
Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH_CONNECT");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT <= 30 /* Android 11.0 (R) */ &&
|
||||
mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
|
||||
Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
|
||||
Log.d(TAG, "Couldn't initialize Bluetooth, this version of Android does not support Bluetooth LE");
|
||||
return;
|
||||
}
|
||||
|
||||
// Find bonded bluetooth controllers and create SteamControllers for them
|
||||
mBluetoothManager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
|
||||
if (mBluetoothManager == null) {
|
||||
// This device doesn't support Bluetooth.
|
||||
return;
|
||||
}
|
||||
|
||||
BluetoothAdapter btAdapter = mBluetoothManager.getAdapter();
|
||||
if (btAdapter == null) {
|
||||
// This device has Bluetooth support in the codebase, but has no available adapters.
|
||||
return;
|
||||
}
|
||||
|
||||
// Get our bonded devices.
|
||||
for (BluetoothDevice device : btAdapter.getBondedDevices()) {
|
||||
|
||||
Log.d(TAG, "Bluetooth device available: " + device);
|
||||
if (isSteamController(device)) {
|
||||
connectBluetoothDevice(device);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// NOTE: These don't work on Chromebooks, to my undying dismay.
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
|
||||
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
|
||||
if (Build.VERSION.SDK_INT >= 33) { /* Android 13.0 (TIRAMISU) */
|
||||
mContext.registerReceiver(mBluetoothBroadcast, filter, Context.RECEIVER_EXPORTED);
|
||||
} else {
|
||||
mContext.registerReceiver(mBluetoothBroadcast, filter);
|
||||
}
|
||||
|
||||
if (mIsChromebook) {
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
mLastBluetoothDevices = new ArrayList<BluetoothDevice>();
|
||||
|
||||
// final HIDDeviceManager finalThis = this;
|
||||
// mHandler.postDelayed(new Runnable() {
|
||||
// @Override
|
||||
// public void run() {
|
||||
// finalThis.chromebookConnectionHandler();
|
||||
// }
|
||||
// }, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
private void shutdownBluetooth() {
|
||||
try {
|
||||
mContext.unregisterReceiver(mBluetoothBroadcast);
|
||||
} catch (Exception e) {
|
||||
// We may not have registered, that's okay
|
||||
}
|
||||
}
|
||||
|
||||
// Chromebooks do not pass along ACTION_ACL_CONNECTED / ACTION_ACL_DISCONNECTED properly.
|
||||
// This function provides a sort of dummy version of that, watching for changes in the
|
||||
// connected devices and attempting to add controllers as things change.
|
||||
void chromebookConnectionHandler() {
|
||||
if (!mIsChromebook) {
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList<BluetoothDevice> disconnected = new ArrayList<BluetoothDevice>();
|
||||
ArrayList<BluetoothDevice> connected = new ArrayList<BluetoothDevice>();
|
||||
|
||||
List<BluetoothDevice> currentConnected = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT);
|
||||
|
||||
for (BluetoothDevice bluetoothDevice : currentConnected) {
|
||||
if (!mLastBluetoothDevices.contains(bluetoothDevice)) {
|
||||
connected.add(bluetoothDevice);
|
||||
}
|
||||
}
|
||||
for (BluetoothDevice bluetoothDevice : mLastBluetoothDevices) {
|
||||
if (!currentConnected.contains(bluetoothDevice)) {
|
||||
disconnected.add(bluetoothDevice);
|
||||
}
|
||||
}
|
||||
|
||||
mLastBluetoothDevices = currentConnected;
|
||||
|
||||
for (BluetoothDevice bluetoothDevice : disconnected) {
|
||||
disconnectBluetoothDevice(bluetoothDevice);
|
||||
}
|
||||
for (BluetoothDevice bluetoothDevice : connected) {
|
||||
connectBluetoothDevice(bluetoothDevice);
|
||||
}
|
||||
|
||||
final HIDDeviceManager finalThis = this;
|
||||
mHandler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
finalThis.chromebookConnectionHandler();
|
||||
}
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
boolean connectBluetoothDevice(BluetoothDevice bluetoothDevice) {
|
||||
Log.v(TAG, "connectBluetoothDevice device=" + bluetoothDevice);
|
||||
synchronized (this) {
|
||||
if (mBluetoothDevices.containsKey(bluetoothDevice)) {
|
||||
Log.v(TAG, "Steam controller with address " + bluetoothDevice + " already exists, attempting reconnect");
|
||||
|
||||
HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
|
||||
device.reconnect();
|
||||
|
||||
return false;
|
||||
}
|
||||
HIDDeviceBLESteamController device = new HIDDeviceBLESteamController(this, bluetoothDevice);
|
||||
int id = device.getId();
|
||||
mBluetoothDevices.put(bluetoothDevice, device);
|
||||
mDevicesById.put(id, device);
|
||||
|
||||
// The Steam Controller will mark itself connected once initialization is complete
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void disconnectBluetoothDevice(BluetoothDevice bluetoothDevice) {
|
||||
synchronized (this) {
|
||||
HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
|
||||
if (device == null)
|
||||
return;
|
||||
|
||||
int id = device.getId();
|
||||
mBluetoothDevices.remove(bluetoothDevice);
|
||||
mDevicesById.remove(id);
|
||||
device.shutdown();
|
||||
HIDDeviceDisconnected(id);
|
||||
}
|
||||
}
|
||||
|
||||
boolean isSteamController(BluetoothDevice bluetoothDevice) {
|
||||
// Sanity check. If you pass in a null device, by definition it is never a Steam Controller.
|
||||
if (bluetoothDevice == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the device has no local name, we really don't want to try an equality check against it.
|
||||
if (bluetoothDevice.getName() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return bluetoothDevice.getName().equals("SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0);
|
||||
}
|
||||
|
||||
private void close() {
|
||||
shutdownUSB();
|
||||
shutdownBluetooth();
|
||||
synchronized (this) {
|
||||
for (HIDDevice device : mDevicesById.values()) {
|
||||
device.shutdown();
|
||||
}
|
||||
mDevicesById.clear();
|
||||
mBluetoothDevices.clear();
|
||||
HIDDeviceReleaseCallback();
|
||||
}
|
||||
}
|
||||
|
||||
public void setFrozen(boolean frozen) {
|
||||
synchronized (this) {
|
||||
for (HIDDevice device : mDevicesById.values()) {
|
||||
device.setFrozen(frozen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private HIDDevice getDevice(int id) {
|
||||
synchronized (this) {
|
||||
HIDDevice result = mDevicesById.get(id);
|
||||
if (result == null) {
|
||||
Log.v(TAG, "No device for id: " + id);
|
||||
Log.v(TAG, "Available devices: " + mDevicesById.keySet());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////// JNI interface functions
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
boolean initialize(boolean usb, boolean bluetooth) {
|
||||
Log.v(TAG, "initialize(" + usb + ", " + bluetooth + ")");
|
||||
|
||||
if (usb) {
|
||||
initializeUSB();
|
||||
}
|
||||
if (bluetooth) {
|
||||
initializeBluetooth();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean openDevice(int deviceID) {
|
||||
Log.v(TAG, "openDevice deviceID=" + deviceID);
|
||||
HIDDevice device = getDevice(deviceID);
|
||||
if (device == null) {
|
||||
HIDDeviceDisconnected(deviceID);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Look to see if this is a USB device and we have permission to access it
|
||||
UsbDevice usbDevice = device.getDevice();
|
||||
if (usbDevice != null && !mUsbManager.hasPermission(usbDevice)) {
|
||||
HIDDeviceOpenPending(deviceID);
|
||||
try {
|
||||
final int FLAG_MUTABLE = 0x02000000; // PendingIntent.FLAG_MUTABLE, but don't require SDK 31
|
||||
int flags;
|
||||
if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) {
|
||||
flags = FLAG_MUTABLE;
|
||||
} else {
|
||||
flags = 0;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(HIDDeviceManager.ACTION_USB_PERMISSION);
|
||||
intent.setPackage(mContext.getPackageName());
|
||||
mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, intent, flags));
|
||||
} catch (Exception e) {
|
||||
Log.v(TAG, "Couldn't request permission for USB device " + usbDevice);
|
||||
HIDDeviceOpenResult(deviceID, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
return device.open();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int writeReport(int deviceID, byte[] report, boolean feature) {
|
||||
try {
|
||||
//Log.v(TAG, "writeReport deviceID=" + deviceID + " length=" + report.length);
|
||||
HIDDevice device;
|
||||
device = getDevice(deviceID);
|
||||
if (device == null) {
|
||||
HIDDeviceDisconnected(deviceID);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return device.writeReport(report, feature);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
boolean readReport(int deviceID, byte[] report, boolean feature) {
|
||||
try {
|
||||
//Log.v(TAG, "readReport deviceID=" + deviceID);
|
||||
HIDDevice device;
|
||||
device = getDevice(deviceID);
|
||||
if (device == null) {
|
||||
HIDDeviceDisconnected(deviceID);
|
||||
return false;
|
||||
}
|
||||
|
||||
return device.readReport(report, feature);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void closeDevice(int deviceID) {
|
||||
try {
|
||||
Log.v(TAG, "closeDevice deviceID=" + deviceID);
|
||||
HIDDevice device;
|
||||
device = getDevice(deviceID);
|
||||
if (device == null) {
|
||||
HIDDeviceDisconnected(deviceID);
|
||||
return;
|
||||
}
|
||||
|
||||
device.close();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////// Native methods
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private native void HIDDeviceRegisterCallback();
|
||||
private native void HIDDeviceReleaseCallback();
|
||||
|
||||
native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number, int interface_class, int interface_subclass, int interface_protocol, boolean bBluetooth);
|
||||
native void HIDDeviceOpenPending(int deviceID);
|
||||
native void HIDDeviceOpenResult(int deviceID, boolean opened);
|
||||
native void HIDDeviceDisconnected(int deviceID);
|
||||
|
||||
native void HIDDeviceInputReport(int deviceID, byte[] report);
|
||||
native void HIDDeviceReportResponse(int deviceID, byte[] report);
|
||||
}
|
||||
@ -0,0 +1,313 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
import android.hardware.usb.*;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
class HIDDeviceUSB implements HIDDevice {
|
||||
|
||||
private static final String TAG = "hidapi";
|
||||
|
||||
protected HIDDeviceManager mManager;
|
||||
protected UsbDevice mDevice;
|
||||
protected int mInterfaceIndex;
|
||||
protected int mInterface;
|
||||
protected int mDeviceId;
|
||||
protected UsbDeviceConnection mConnection;
|
||||
protected UsbEndpoint mInputEndpoint;
|
||||
protected UsbEndpoint mOutputEndpoint;
|
||||
protected InputThread mInputThread;
|
||||
protected boolean mRunning;
|
||||
protected boolean mFrozen;
|
||||
|
||||
public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_index) {
|
||||
mManager = manager;
|
||||
mDevice = usbDevice;
|
||||
mInterfaceIndex = interface_index;
|
||||
mInterface = mDevice.getInterface(mInterfaceIndex).getId();
|
||||
mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier());
|
||||
mRunning = false;
|
||||
}
|
||||
|
||||
String getIdentifier() {
|
||||
return String.format(Locale.ENGLISH, "%s/%x/%x/%d", mDevice.getDeviceName(), mDevice.getVendorId(), mDevice.getProductId(), mInterfaceIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return mDeviceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVendorId() {
|
||||
return mDevice.getVendorId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProductId() {
|
||||
return mDevice.getProductId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSerialNumber() {
|
||||
String result = null;
|
||||
try {
|
||||
result = mDevice.getSerialNumber();
|
||||
}
|
||||
catch (SecurityException exception) {
|
||||
//Log.w(TAG, "App permissions mean we cannot get serial number for device " + getDeviceName() + " message: " + exception.getMessage());
|
||||
}
|
||||
if (result == null) {
|
||||
result = "";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getManufacturerName() {
|
||||
String result;
|
||||
result = mDevice.getManufacturerName();
|
||||
if (result == null) {
|
||||
result = String.format("%x", getVendorId());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProductName() {
|
||||
String result;
|
||||
result = mDevice.getProductName();
|
||||
if (result == null) {
|
||||
result = String.format("%x", getProductId());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UsbDevice getDevice() {
|
||||
return mDevice;
|
||||
}
|
||||
|
||||
String getDeviceName() {
|
||||
return getManufacturerName() + " " + getProductName() + "(0x" + String.format("%x", getVendorId()) + "/0x" + String.format("%x", getProductId()) + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean open() {
|
||||
mConnection = mManager.getUSBManager().openDevice(mDevice);
|
||||
if (mConnection == null) {
|
||||
Log.w(TAG, "Unable to open USB device " + getDeviceName());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Force claim our interface
|
||||
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
|
||||
if (!mConnection.claimInterface(iface, true)) {
|
||||
Log.w(TAG, "Failed to claim interfaces on USB device " + getDeviceName());
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the endpoints
|
||||
for (int j = 0; j < iface.getEndpointCount(); j++) {
|
||||
UsbEndpoint endpt = iface.getEndpoint(j);
|
||||
switch (endpt.getDirection()) {
|
||||
case UsbConstants.USB_DIR_IN:
|
||||
if (mInputEndpoint == null) {
|
||||
mInputEndpoint = endpt;
|
||||
}
|
||||
break;
|
||||
case UsbConstants.USB_DIR_OUT:
|
||||
if (mOutputEndpoint == null) {
|
||||
mOutputEndpoint = endpt;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the required endpoints were present
|
||||
if (mInputEndpoint == null || mOutputEndpoint == null) {
|
||||
Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName());
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start listening for input
|
||||
mRunning = true;
|
||||
mInputThread = new InputThread();
|
||||
mInputThread.start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeReport(byte[] report, boolean feature) {
|
||||
if (mConnection == null) {
|
||||
Log.w(TAG, "writeReport() called with no device connection");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (feature) {
|
||||
int res = -1;
|
||||
int offset = 0;
|
||||
int length = report.length;
|
||||
boolean skipped_report_id = false;
|
||||
byte report_number = report[0];
|
||||
|
||||
if (report_number == 0x0) {
|
||||
++offset;
|
||||
--length;
|
||||
skipped_report_id = true;
|
||||
}
|
||||
|
||||
res = mConnection.controlTransfer(
|
||||
UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_OUT,
|
||||
0x09/*HID set_report*/,
|
||||
(3/*HID feature*/ << 8) | report_number,
|
||||
mInterface,
|
||||
report, offset, length,
|
||||
1000/*timeout millis*/);
|
||||
|
||||
if (res < 0) {
|
||||
Log.w(TAG, "writeFeatureReport() returned " + res + " on device " + getDeviceName());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (skipped_report_id) {
|
||||
++length;
|
||||
}
|
||||
return length;
|
||||
} else {
|
||||
int res = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000);
|
||||
if (res != report.length) {
|
||||
Log.w(TAG, "writeOutputReport() returned " + res + " on device " + getDeviceName());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readReport(byte[] report, boolean feature) {
|
||||
int res = -1;
|
||||
int offset = 0;
|
||||
int length = report.length;
|
||||
boolean skipped_report_id = false;
|
||||
byte report_number = report[0];
|
||||
|
||||
if (mConnection == null) {
|
||||
Log.w(TAG, "readReport() called with no device connection");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (report_number == 0x0) {
|
||||
/* Offset the return buffer by 1, so that the report ID
|
||||
will remain in byte 0. */
|
||||
++offset;
|
||||
--length;
|
||||
skipped_report_id = true;
|
||||
}
|
||||
|
||||
res = mConnection.controlTransfer(
|
||||
UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_IN,
|
||||
0x01/*HID get_report*/,
|
||||
((feature ? 3/*HID feature*/ : 1/*HID Input*/) << 8) | report_number,
|
||||
mInterface,
|
||||
report, offset, length,
|
||||
1000/*timeout millis*/);
|
||||
|
||||
if (res < 0) {
|
||||
Log.w(TAG, "getFeatureReport() returned " + res + " on device " + getDeviceName());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (skipped_report_id) {
|
||||
++res;
|
||||
++length;
|
||||
}
|
||||
|
||||
byte[] data;
|
||||
if (res == length) {
|
||||
data = report;
|
||||
} else {
|
||||
data = Arrays.copyOfRange(report, 0, res);
|
||||
}
|
||||
mManager.HIDDeviceReportResponse(mDeviceId, data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
mRunning = false;
|
||||
if (mInputThread != null) {
|
||||
while (mInputThread.isAlive()) {
|
||||
mInputThread.interrupt();
|
||||
try {
|
||||
mInputThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
// Keep trying until we're done
|
||||
}
|
||||
}
|
||||
mInputThread = null;
|
||||
}
|
||||
if (mConnection != null) {
|
||||
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
|
||||
mConnection.releaseInterface(iface);
|
||||
mConnection.close();
|
||||
mConnection = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
close();
|
||||
mManager = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFrozen(boolean frozen) {
|
||||
mFrozen = frozen;
|
||||
}
|
||||
|
||||
protected class InputThread extends Thread {
|
||||
@Override
|
||||
public void run() {
|
||||
int packetSize = mInputEndpoint.getMaxPacketSize();
|
||||
byte[] packet = new byte[packetSize];
|
||||
while (mRunning) {
|
||||
int r;
|
||||
try
|
||||
{
|
||||
r = mConnection.bulkTransfer(mInputEndpoint, packet, packetSize, 1000);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.v(TAG, "Exception in UsbDeviceConnection bulktransfer: " + e);
|
||||
break;
|
||||
}
|
||||
if (r < 0) {
|
||||
// Could be a timeout or an I/O error
|
||||
}
|
||||
if (r > 0) {
|
||||
byte[] data;
|
||||
if (r == packetSize) {
|
||||
data = packet;
|
||||
} else {
|
||||
data = Arrays.copyOfRange(packet, 0, r);
|
||||
}
|
||||
|
||||
if (!mFrozen) {
|
||||
mManager.HIDDeviceInputReport(mDeviceId, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,90 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
SDL library initialization
|
||||
*/
|
||||
public class SDL {
|
||||
|
||||
// This function should be called first and sets up the native code
|
||||
// so it can call into the Java classes
|
||||
static public void setupJNI() {
|
||||
SDLActivity.nativeSetupJNI();
|
||||
SDLAudioManager.nativeSetupJNI();
|
||||
SDLControllerManager.nativeSetupJNI();
|
||||
}
|
||||
|
||||
// This function should be called each time the activity is started
|
||||
static public void initialize() {
|
||||
setContext(null);
|
||||
|
||||
SDLActivity.initialize();
|
||||
SDLAudioManager.initialize();
|
||||
SDLControllerManager.initialize();
|
||||
}
|
||||
|
||||
// This function stores the current activity (SDL or not)
|
||||
static public void setContext(Activity context) {
|
||||
SDLAudioManager.setContext(context);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
static public Activity getContext() {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
static void loadLibrary(String libraryName) throws UnsatisfiedLinkError, SecurityException, NullPointerException {
|
||||
loadLibrary(libraryName, mContext);
|
||||
}
|
||||
|
||||
static void loadLibrary(String libraryName, Context context) throws UnsatisfiedLinkError, SecurityException, NullPointerException {
|
||||
|
||||
if (libraryName == null) {
|
||||
throw new NullPointerException("No library name provided.");
|
||||
}
|
||||
|
||||
try {
|
||||
// Let's see if we have ReLinker available in the project. This is necessary for
|
||||
// some projects that have huge numbers of local libraries bundled, and thus may
|
||||
// trip a bug in Android's native library loader which ReLinker works around. (If
|
||||
// loadLibrary works properly, ReLinker will simply use the normal Android method
|
||||
// internally.)
|
||||
//
|
||||
// To use ReLinker, just add it as a dependency. For more information, see
|
||||
// https://github.com/KeepSafe/ReLinker for ReLinker's repository.
|
||||
//
|
||||
Class<?> relinkClass = context.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker");
|
||||
Class<?> relinkListenerClass = context.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker$LoadListener");
|
||||
Class<?> contextClass = context.getClassLoader().loadClass("android.content.Context");
|
||||
Class<?> stringClass = context.getClassLoader().loadClass("java.lang.String");
|
||||
|
||||
// Get a 'force' instance of the ReLinker, so we can ensure libraries are reinstalled if
|
||||
// they've changed during updates.
|
||||
Method forceMethod = relinkClass.getDeclaredMethod("force");
|
||||
Object relinkInstance = forceMethod.invoke(null);
|
||||
Class<?> relinkInstanceClass = relinkInstance.getClass();
|
||||
|
||||
// Actually load the library!
|
||||
Method loadMethod = relinkInstanceClass.getDeclaredMethod("loadLibrary", contextClass, stringClass, stringClass, relinkListenerClass);
|
||||
loadMethod.invoke(relinkInstance, context, libraryName, null, null);
|
||||
}
|
||||
catch (final Throwable e) {
|
||||
// Fall back
|
||||
try {
|
||||
System.loadLibrary(libraryName);
|
||||
}
|
||||
catch (final UnsatisfiedLinkError ule) {
|
||||
throw ule;
|
||||
}
|
||||
catch (final SecurityException se) {
|
||||
throw se;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static Activity mContext;
|
||||
}
|
||||
@ -0,0 +1,126 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.AudioDeviceCallback;
|
||||
import android.media.AudioDeviceInfo;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
|
||||
class SDLAudioManager {
|
||||
protected static final String TAG = "SDLAudio";
|
||||
|
||||
protected static Context mContext;
|
||||
|
||||
private static AudioDeviceCallback mAudioDeviceCallback;
|
||||
|
||||
static void initialize() {
|
||||
mAudioDeviceCallback = null;
|
||||
|
||||
if(Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */)
|
||||
{
|
||||
mAudioDeviceCallback = new AudioDeviceCallback() {
|
||||
@Override
|
||||
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
|
||||
for (AudioDeviceInfo deviceInfo : addedDevices) {
|
||||
nativeAddAudioDevice(deviceInfo.isSink(), deviceInfo.getProductName().toString(), deviceInfo.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
|
||||
for (AudioDeviceInfo deviceInfo : removedDevices) {
|
||||
nativeRemoveAudioDevice(deviceInfo.isSink(), deviceInfo.getId());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static void setContext(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
static void release(Context context) {
|
||||
// no-op atm
|
||||
}
|
||||
|
||||
// Audio
|
||||
|
||||
private static AudioDeviceInfo getInputAudioDeviceInfo(int deviceId) {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
for (AudioDeviceInfo deviceInfo : audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)) {
|
||||
if (deviceInfo.getId() == deviceId) {
|
||||
return deviceInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static AudioDeviceInfo getPlaybackAudioDeviceInfo(int deviceId) {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
for (AudioDeviceInfo deviceInfo : audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)) {
|
||||
if (deviceInfo.getId() == deviceId) {
|
||||
return deviceInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static void registerAudioDeviceCallback() {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
// get an initial list now, before hotplug callbacks fire.
|
||||
for (AudioDeviceInfo dev : audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)) {
|
||||
if (dev.getType() == AudioDeviceInfo.TYPE_TELEPHONY) {
|
||||
continue; // Device cannot be opened
|
||||
}
|
||||
nativeAddAudioDevice(dev.isSink(), dev.getProductName().toString(), dev.getId());
|
||||
}
|
||||
for (AudioDeviceInfo dev : audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)) {
|
||||
nativeAddAudioDevice(dev.isSink(), dev.getProductName().toString(), dev.getId());
|
||||
}
|
||||
audioManager.registerAudioDeviceCallback(mAudioDeviceCallback, null);
|
||||
}
|
||||
}
|
||||
|
||||
static void unregisterAudioDeviceCallback() {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
audioManager.unregisterAudioDeviceCallback(mAudioDeviceCallback);
|
||||
}
|
||||
}
|
||||
|
||||
/** This method is called by SDL using JNI. */
|
||||
static void audioSetThreadPriority(boolean recording, int device_id) {
|
||||
try {
|
||||
|
||||
/* Set thread name */
|
||||
if (recording) {
|
||||
Thread.currentThread().setName("SDLAudioC" + device_id);
|
||||
} else {
|
||||
Thread.currentThread().setName("SDLAudioP" + device_id);
|
||||
}
|
||||
|
||||
/* Set thread priority */
|
||||
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.v(TAG, "modify thread properties failed " + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
static native void nativeSetupJNI();
|
||||
|
||||
static native void nativeRemoveAudioDevice(boolean recording, int deviceId);
|
||||
|
||||
static native void nativeAddAudioDevice(boolean recording, String name, int deviceId);
|
||||
|
||||
}
|
||||
@ -0,0 +1,877 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.VibrationEffect;
|
||||
import android.os.Vibrator;
|
||||
import android.os.VibratorManager;
|
||||
import android.util.Log;
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
|
||||
public class SDLControllerManager
|
||||
{
|
||||
|
||||
static native void nativeSetupJNI();
|
||||
|
||||
static native void nativeAddJoystick(int device_id, String name, String desc,
|
||||
int vendor_id, int product_id,
|
||||
int button_mask,
|
||||
int naxes, int axis_mask, int nhats, boolean can_rumble);
|
||||
static native void nativeRemoveJoystick(int device_id);
|
||||
static native void nativeAddHaptic(int device_id, String name);
|
||||
static native void nativeRemoveHaptic(int device_id);
|
||||
static public native boolean onNativePadDown(int device_id, int keycode);
|
||||
static public native boolean onNativePadUp(int device_id, int keycode);
|
||||
static native void onNativeJoy(int device_id, int axis,
|
||||
float value);
|
||||
static native void onNativeHat(int device_id, int hat_id,
|
||||
int x, int y);
|
||||
|
||||
protected static SDLJoystickHandler mJoystickHandler;
|
||||
protected static SDLHapticHandler mHapticHandler;
|
||||
|
||||
private static final String TAG = "SDLControllerManager";
|
||||
|
||||
static void initialize() {
|
||||
if (mJoystickHandler == null) {
|
||||
mJoystickHandler = new SDLJoystickHandler();
|
||||
}
|
||||
|
||||
if (mHapticHandler == null) {
|
||||
if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) {
|
||||
mHapticHandler = new SDLHapticHandler_API31();
|
||||
} else if (Build.VERSION.SDK_INT >= 26 /* Android 8.0 (O) */) {
|
||||
mHapticHandler = new SDLHapticHandler_API26();
|
||||
} else {
|
||||
mHapticHandler = new SDLHapticHandler();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
|
||||
static public boolean handleJoystickMotionEvent(MotionEvent event) {
|
||||
return mJoystickHandler.handleMotionEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
static void pollInputDevices() {
|
||||
mJoystickHandler.pollInputDevices();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
static void pollHapticDevices() {
|
||||
mHapticHandler.pollHapticDevices();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
static void hapticRun(int device_id, float intensity, int length) {
|
||||
mHapticHandler.run(device_id, intensity, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
static void hapticRumble(int device_id, float low_frequency_intensity, float high_frequency_intensity, int length) {
|
||||
mHapticHandler.rumble(device_id, low_frequency_intensity, high_frequency_intensity, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
static void hapticStop(int device_id)
|
||||
{
|
||||
mHapticHandler.stop(device_id);
|
||||
}
|
||||
|
||||
// Check if a given device is considered a possible SDL joystick
|
||||
static public boolean isDeviceSDLJoystick(int deviceId) {
|
||||
InputDevice device = InputDevice.getDevice(deviceId);
|
||||
// We cannot use InputDevice.isVirtual before API 16, so let's accept
|
||||
// only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
|
||||
if ((device == null) || (deviceId < 0)) {
|
||||
return false;
|
||||
}
|
||||
int sources = device.getSources();
|
||||
|
||||
/* This is called for every button press, so let's not spam the logs */
|
||||
/*
|
||||
if ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
|
||||
Log.v(TAG, "Input device " + device.getName() + " has class joystick.");
|
||||
}
|
||||
if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) {
|
||||
Log.v(TAG, "Input device " + device.getName() + " is a dpad.");
|
||||
}
|
||||
if ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
|
||||
Log.v(TAG, "Input device " + device.getName() + " is a gamepad.");
|
||||
}
|
||||
*/
|
||||
|
||||
return ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 ||
|
||||
((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
|
||||
((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Actual joystick functionality available for API >= 19 devices */
|
||||
class SDLJoystickHandler {
|
||||
|
||||
static class SDLJoystick {
|
||||
int device_id;
|
||||
String name;
|
||||
String desc;
|
||||
ArrayList<InputDevice.MotionRange> axes;
|
||||
ArrayList<InputDevice.MotionRange> hats;
|
||||
}
|
||||
static class RangeComparator implements Comparator<InputDevice.MotionRange> {
|
||||
@Override
|
||||
public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
|
||||
// Some controllers, like the Moga Pro 2, return AXIS_GAS (22) for right trigger and AXIS_BRAKE (23) for left trigger - swap them so they're sorted in the right order for SDL
|
||||
int arg0Axis = arg0.getAxis();
|
||||
int arg1Axis = arg1.getAxis();
|
||||
if (arg0Axis == MotionEvent.AXIS_GAS) {
|
||||
arg0Axis = MotionEvent.AXIS_BRAKE;
|
||||
} else if (arg0Axis == MotionEvent.AXIS_BRAKE) {
|
||||
arg0Axis = MotionEvent.AXIS_GAS;
|
||||
}
|
||||
if (arg1Axis == MotionEvent.AXIS_GAS) {
|
||||
arg1Axis = MotionEvent.AXIS_BRAKE;
|
||||
} else if (arg1Axis == MotionEvent.AXIS_BRAKE) {
|
||||
arg1Axis = MotionEvent.AXIS_GAS;
|
||||
}
|
||||
|
||||
// Make sure the AXIS_Z is sorted between AXIS_RY and AXIS_RZ.
|
||||
// This is because the usual pairing are:
|
||||
// - AXIS_X + AXIS_Y (left stick).
|
||||
// - AXIS_RX, AXIS_RY (sometimes the right stick, sometimes triggers).
|
||||
// - AXIS_Z, AXIS_RZ (sometimes the right stick, sometimes triggers).
|
||||
// This sorts the axes in the above order, which tends to be correct
|
||||
// for Xbox-ish game pads that have the right stick on RX/RY and the
|
||||
// triggers on Z/RZ.
|
||||
//
|
||||
// Gamepads that don't have AXIS_Z/AXIS_RZ but use
|
||||
// AXIS_LTRIGGER/AXIS_RTRIGGER are unaffected by this.
|
||||
//
|
||||
// References:
|
||||
// - https://developer.android.com/develop/ui/views/touch-and-input/game-controllers/controller-input
|
||||
// - https://www.kernel.org/doc/html/latest/input/gamepad.html
|
||||
if (arg0Axis == MotionEvent.AXIS_Z) {
|
||||
arg0Axis = MotionEvent.AXIS_RZ - 1;
|
||||
} else if (arg0Axis > MotionEvent.AXIS_Z && arg0Axis < MotionEvent.AXIS_RZ) {
|
||||
--arg0Axis;
|
||||
}
|
||||
if (arg1Axis == MotionEvent.AXIS_Z) {
|
||||
arg1Axis = MotionEvent.AXIS_RZ - 1;
|
||||
} else if (arg1Axis > MotionEvent.AXIS_Z && arg1Axis < MotionEvent.AXIS_RZ) {
|
||||
--arg1Axis;
|
||||
}
|
||||
|
||||
return arg0Axis - arg1Axis;
|
||||
}
|
||||
}
|
||||
|
||||
private final ArrayList<SDLJoystick> mJoysticks;
|
||||
|
||||
SDLJoystickHandler() {
|
||||
|
||||
mJoysticks = new ArrayList<SDLJoystick>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles adding and removing of input devices.
|
||||
*/
|
||||
void pollInputDevices() {
|
||||
int[] deviceIds = InputDevice.getDeviceIds();
|
||||
|
||||
for (int device_id : deviceIds) {
|
||||
if (SDLControllerManager.isDeviceSDLJoystick(device_id)) {
|
||||
SDLJoystick joystick = getJoystick(device_id);
|
||||
if (joystick == null) {
|
||||
InputDevice joystickDevice = InputDevice.getDevice(device_id);
|
||||
joystick = new SDLJoystick();
|
||||
joystick.device_id = device_id;
|
||||
joystick.name = joystickDevice.getName();
|
||||
joystick.desc = getJoystickDescriptor(joystickDevice);
|
||||
joystick.axes = new ArrayList<InputDevice.MotionRange>();
|
||||
joystick.hats = new ArrayList<InputDevice.MotionRange>();
|
||||
|
||||
List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
|
||||
Collections.sort(ranges, new RangeComparator());
|
||||
for (InputDevice.MotionRange range : ranges) {
|
||||
if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
|
||||
if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) {
|
||||
joystick.hats.add(range);
|
||||
} else {
|
||||
joystick.axes.add(range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean can_rumble = false;
|
||||
if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) {
|
||||
VibratorManager manager = joystickDevice.getVibratorManager();
|
||||
int[] vibrators = manager.getVibratorIds();
|
||||
if (vibrators.length > 0) {
|
||||
can_rumble = true;
|
||||
}
|
||||
}
|
||||
|
||||
mJoysticks.add(joystick);
|
||||
SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc,
|
||||
getVendorId(joystickDevice), getProductId(joystickDevice),
|
||||
getButtonMask(joystickDevice), joystick.axes.size(), getAxisMask(joystick.axes), joystick.hats.size()/2, can_rumble);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check removed devices */
|
||||
ArrayList<Integer> removedDevices = null;
|
||||
for (SDLJoystick joystick : mJoysticks) {
|
||||
int device_id = joystick.device_id;
|
||||
int i;
|
||||
for (i = 0; i < deviceIds.length; i++) {
|
||||
if (device_id == deviceIds[i]) break;
|
||||
}
|
||||
if (i == deviceIds.length) {
|
||||
if (removedDevices == null) {
|
||||
removedDevices = new ArrayList<Integer>();
|
||||
}
|
||||
removedDevices.add(device_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (removedDevices != null) {
|
||||
for (int device_id : removedDevices) {
|
||||
SDLControllerManager.nativeRemoveJoystick(device_id);
|
||||
for (int i = 0; i < mJoysticks.size(); i++) {
|
||||
if (mJoysticks.get(i).device_id == device_id) {
|
||||
mJoysticks.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected SDLJoystick getJoystick(int device_id) {
|
||||
for (SDLJoystick joystick : mJoysticks) {
|
||||
if (joystick.device_id == device_id) {
|
||||
return joystick;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles given MotionEvent.
|
||||
* @param event the event to be handled.
|
||||
* @return if given event was processed.
|
||||
*/
|
||||
boolean handleMotionEvent(MotionEvent event) {
|
||||
int actionPointerIndex = event.getActionIndex();
|
||||
int action = event.getActionMasked();
|
||||
if (action == MotionEvent.ACTION_MOVE) {
|
||||
SDLJoystick joystick = getJoystick(event.getDeviceId());
|
||||
if (joystick != null) {
|
||||
for (int i = 0; i < joystick.axes.size(); i++) {
|
||||
InputDevice.MotionRange range = joystick.axes.get(i);
|
||||
/* Normalize the value to -1...1 */
|
||||
float value = (event.getAxisValue(range.getAxis(), actionPointerIndex) - range.getMin()) / range.getRange() * 2.0f - 1.0f;
|
||||
SDLControllerManager.onNativeJoy(joystick.device_id, i, value);
|
||||
}
|
||||
for (int i = 0; i < joystick.hats.size() / 2; i++) {
|
||||
int hatX = Math.round(event.getAxisValue(joystick.hats.get(2 * i).getAxis(), actionPointerIndex));
|
||||
int hatY = Math.round(event.getAxisValue(joystick.hats.get(2 * i + 1).getAxis(), actionPointerIndex));
|
||||
SDLControllerManager.onNativeHat(joystick.device_id, i, hatX, hatY);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
String getJoystickDescriptor(InputDevice joystickDevice) {
|
||||
String desc = joystickDevice.getDescriptor();
|
||||
|
||||
if (desc != null && !desc.isEmpty()) {
|
||||
return desc;
|
||||
}
|
||||
|
||||
return joystickDevice.getName();
|
||||
}
|
||||
|
||||
int getProductId(InputDevice joystickDevice) {
|
||||
return joystickDevice.getProductId();
|
||||
}
|
||||
|
||||
int getVendorId(InputDevice joystickDevice) {
|
||||
return joystickDevice.getVendorId();
|
||||
}
|
||||
|
||||
int getAxisMask(List<InputDevice.MotionRange> ranges) {
|
||||
// For compatibility, keep computing the axis mask like before,
|
||||
// only really distinguishing 2, 4 and 6 axes.
|
||||
int axis_mask = 0;
|
||||
if (ranges.size() >= 2) {
|
||||
// ((1 << SDL_GAMEPAD_AXIS_LEFTX) | (1 << SDL_GAMEPAD_AXIS_LEFTY))
|
||||
axis_mask |= 0x0003;
|
||||
}
|
||||
if (ranges.size() >= 4) {
|
||||
// ((1 << SDL_GAMEPAD_AXIS_RIGHTX) | (1 << SDL_GAMEPAD_AXIS_RIGHTY))
|
||||
axis_mask |= 0x000c;
|
||||
}
|
||||
if (ranges.size() >= 6) {
|
||||
// ((1 << SDL_GAMEPAD_AXIS_LEFT_TRIGGER) | (1 << SDL_GAMEPAD_AXIS_RIGHT_TRIGGER))
|
||||
axis_mask |= 0x0030;
|
||||
}
|
||||
// Also add an indicator bit for whether the sorting order has changed.
|
||||
// This serves to disable outdated gamecontrollerdb.txt mappings.
|
||||
boolean have_z = false;
|
||||
boolean have_past_z_before_rz = false;
|
||||
for (InputDevice.MotionRange range : ranges) {
|
||||
int axis = range.getAxis();
|
||||
if (axis == MotionEvent.AXIS_Z) {
|
||||
have_z = true;
|
||||
} else if (axis > MotionEvent.AXIS_Z && axis < MotionEvent.AXIS_RZ) {
|
||||
have_past_z_before_rz = true;
|
||||
}
|
||||
}
|
||||
if (have_z && have_past_z_before_rz) {
|
||||
// If both these exist, the compare() function changed sorting order.
|
||||
// Set a bit to indicate this fact.
|
||||
axis_mask |= 0x8000;
|
||||
}
|
||||
return axis_mask;
|
||||
}
|
||||
|
||||
int getButtonMask(InputDevice joystickDevice) {
|
||||
int button_mask = 0;
|
||||
int[] keys = new int[] {
|
||||
KeyEvent.KEYCODE_BUTTON_A,
|
||||
KeyEvent.KEYCODE_BUTTON_B,
|
||||
KeyEvent.KEYCODE_BUTTON_X,
|
||||
KeyEvent.KEYCODE_BUTTON_Y,
|
||||
KeyEvent.KEYCODE_BACK,
|
||||
KeyEvent.KEYCODE_MENU,
|
||||
KeyEvent.KEYCODE_BUTTON_MODE,
|
||||
KeyEvent.KEYCODE_BUTTON_START,
|
||||
KeyEvent.KEYCODE_BUTTON_THUMBL,
|
||||
KeyEvent.KEYCODE_BUTTON_THUMBR,
|
||||
KeyEvent.KEYCODE_BUTTON_L1,
|
||||
KeyEvent.KEYCODE_BUTTON_R1,
|
||||
KeyEvent.KEYCODE_DPAD_UP,
|
||||
KeyEvent.KEYCODE_DPAD_DOWN,
|
||||
KeyEvent.KEYCODE_DPAD_LEFT,
|
||||
KeyEvent.KEYCODE_DPAD_RIGHT,
|
||||
KeyEvent.KEYCODE_BUTTON_SELECT,
|
||||
KeyEvent.KEYCODE_DPAD_CENTER,
|
||||
|
||||
// These don't map into any SDL controller buttons directly
|
||||
KeyEvent.KEYCODE_BUTTON_L2,
|
||||
KeyEvent.KEYCODE_BUTTON_R2,
|
||||
KeyEvent.KEYCODE_BUTTON_C,
|
||||
KeyEvent.KEYCODE_BUTTON_Z,
|
||||
KeyEvent.KEYCODE_BUTTON_1,
|
||||
KeyEvent.KEYCODE_BUTTON_2,
|
||||
KeyEvent.KEYCODE_BUTTON_3,
|
||||
KeyEvent.KEYCODE_BUTTON_4,
|
||||
KeyEvent.KEYCODE_BUTTON_5,
|
||||
KeyEvent.KEYCODE_BUTTON_6,
|
||||
KeyEvent.KEYCODE_BUTTON_7,
|
||||
KeyEvent.KEYCODE_BUTTON_8,
|
||||
KeyEvent.KEYCODE_BUTTON_9,
|
||||
KeyEvent.KEYCODE_BUTTON_10,
|
||||
KeyEvent.KEYCODE_BUTTON_11,
|
||||
KeyEvent.KEYCODE_BUTTON_12,
|
||||
KeyEvent.KEYCODE_BUTTON_13,
|
||||
KeyEvent.KEYCODE_BUTTON_14,
|
||||
KeyEvent.KEYCODE_BUTTON_15,
|
||||
KeyEvent.KEYCODE_BUTTON_16,
|
||||
};
|
||||
int[] masks = new int[] {
|
||||
(1 << 0), // A -> A
|
||||
(1 << 1), // B -> B
|
||||
(1 << 2), // X -> X
|
||||
(1 << 3), // Y -> Y
|
||||
(1 << 4), // BACK -> BACK
|
||||
(1 << 6), // MENU -> START
|
||||
(1 << 5), // MODE -> GUIDE
|
||||
(1 << 6), // START -> START
|
||||
(1 << 7), // THUMBL -> LEFTSTICK
|
||||
(1 << 8), // THUMBR -> RIGHTSTICK
|
||||
(1 << 9), // L1 -> LEFTSHOULDER
|
||||
(1 << 10), // R1 -> RIGHTSHOULDER
|
||||
(1 << 11), // DPAD_UP -> DPAD_UP
|
||||
(1 << 12), // DPAD_DOWN -> DPAD_DOWN
|
||||
(1 << 13), // DPAD_LEFT -> DPAD_LEFT
|
||||
(1 << 14), // DPAD_RIGHT -> DPAD_RIGHT
|
||||
(1 << 4), // SELECT -> BACK
|
||||
(1 << 0), // DPAD_CENTER -> A
|
||||
(1 << 15), // L2 -> ??
|
||||
(1 << 16), // R2 -> ??
|
||||
(1 << 17), // C -> ??
|
||||
(1 << 18), // Z -> ??
|
||||
(1 << 20), // 1 -> ??
|
||||
(1 << 21), // 2 -> ??
|
||||
(1 << 22), // 3 -> ??
|
||||
(1 << 23), // 4 -> ??
|
||||
(1 << 24), // 5 -> ??
|
||||
(1 << 25), // 6 -> ??
|
||||
(1 << 26), // 7 -> ??
|
||||
(1 << 27), // 8 -> ??
|
||||
(1 << 28), // 9 -> ??
|
||||
(1 << 29), // 10 -> ??
|
||||
(1 << 30), // 11 -> ??
|
||||
(1 << 31), // 12 -> ??
|
||||
// We're out of room...
|
||||
0xFFFFFFFF, // 13 -> ??
|
||||
0xFFFFFFFF, // 14 -> ??
|
||||
0xFFFFFFFF, // 15 -> ??
|
||||
0xFFFFFFFF, // 16 -> ??
|
||||
};
|
||||
boolean[] has_keys = joystickDevice.hasKeys(keys);
|
||||
for (int i = 0; i < keys.length; ++i) {
|
||||
if (has_keys[i]) {
|
||||
button_mask |= masks[i];
|
||||
}
|
||||
}
|
||||
return button_mask;
|
||||
}
|
||||
}
|
||||
|
||||
class SDLHapticHandler_API31 extends SDLHapticHandler {
|
||||
@Override
|
||||
void run(int device_id, float intensity, int length) {
|
||||
SDLHaptic haptic = getHaptic(device_id);
|
||||
if (haptic != null) {
|
||||
vibrate(haptic.vib, intensity, length);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void rumble(int device_id, float low_frequency_intensity, float high_frequency_intensity, int length) {
|
||||
InputDevice device = InputDevice.getDevice(device_id);
|
||||
if (device == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < 31 /* Android 12.0 (S) */) {
|
||||
/* Silence 'lint' warning */
|
||||
return;
|
||||
}
|
||||
|
||||
VibratorManager manager = device.getVibratorManager();
|
||||
int[] vibrators = manager.getVibratorIds();
|
||||
if (vibrators.length >= 2) {
|
||||
vibrate(manager.getVibrator(vibrators[0]), low_frequency_intensity, length);
|
||||
vibrate(manager.getVibrator(vibrators[1]), high_frequency_intensity, length);
|
||||
} else if (vibrators.length == 1) {
|
||||
float intensity = (low_frequency_intensity * 0.6f) + (high_frequency_intensity * 0.4f);
|
||||
vibrate(manager.getVibrator(vibrators[0]), intensity, length);
|
||||
}
|
||||
}
|
||||
|
||||
private void vibrate(Vibrator vibrator, float intensity, int length) {
|
||||
|
||||
if (Build.VERSION.SDK_INT < 31 /* Android 12.0 (S) */) {
|
||||
/* Silence 'lint' warning */
|
||||
return;
|
||||
}
|
||||
|
||||
if (intensity == 0.0f) {
|
||||
vibrator.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
int value = Math.round(intensity * 255);
|
||||
if (value > 255) {
|
||||
value = 255;
|
||||
}
|
||||
if (value < 1) {
|
||||
vibrator.cancel();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
vibrator.vibrate(VibrationEffect.createOneShot(length, value));
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Fall back to the generic method, which uses DEFAULT_AMPLITUDE, but works even if
|
||||
// something went horribly wrong with the Android 8.0 APIs.
|
||||
vibrator.vibrate(length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SDLHapticHandler_API26 extends SDLHapticHandler {
|
||||
@Override
|
||||
void run(int device_id, float intensity, int length) {
|
||||
|
||||
if (Build.VERSION.SDK_INT < 26 /* Android 8.0 (O) */) {
|
||||
/* Silence 'lint' warning */
|
||||
return;
|
||||
}
|
||||
|
||||
SDLHaptic haptic = getHaptic(device_id);
|
||||
if (haptic != null) {
|
||||
if (intensity == 0.0f) {
|
||||
stop(device_id);
|
||||
return;
|
||||
}
|
||||
|
||||
int vibeValue = Math.round(intensity * 255);
|
||||
|
||||
if (vibeValue > 255) {
|
||||
vibeValue = 255;
|
||||
}
|
||||
if (vibeValue < 1) {
|
||||
stop(device_id);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
haptic.vib.vibrate(VibrationEffect.createOneShot(length, vibeValue));
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Fall back to the generic method, which uses DEFAULT_AMPLITUDE, but works even if
|
||||
// something went horribly wrong with the Android 8.0 APIs.
|
||||
haptic.vib.vibrate(length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SDLHapticHandler {
|
||||
|
||||
static class SDLHaptic {
|
||||
int device_id;
|
||||
String name;
|
||||
Vibrator vib;
|
||||
}
|
||||
|
||||
private final ArrayList<SDLHaptic> mHaptics;
|
||||
|
||||
SDLHapticHandler() {
|
||||
mHaptics = new ArrayList<SDLHaptic>();
|
||||
}
|
||||
|
||||
void run(int device_id, float intensity, int length) {
|
||||
SDLHaptic haptic = getHaptic(device_id);
|
||||
if (haptic != null) {
|
||||
haptic.vib.vibrate(length);
|
||||
}
|
||||
}
|
||||
|
||||
void rumble(int device_id, float low_frequency_intensity, float high_frequency_intensity, int length) {
|
||||
// Not supported in older APIs
|
||||
}
|
||||
|
||||
void stop(int device_id) {
|
||||
SDLHaptic haptic = getHaptic(device_id);
|
||||
if (haptic != null) {
|
||||
haptic.vib.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void pollHapticDevices() {
|
||||
|
||||
final int deviceId_VIBRATOR_SERVICE = 999999;
|
||||
boolean hasVibratorService = false;
|
||||
|
||||
/* Check VIBRATOR_SERVICE */
|
||||
Vibrator vib = (Vibrator) SDL.getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
||||
if (vib != null) {
|
||||
hasVibratorService = vib.hasVibrator();
|
||||
|
||||
if (hasVibratorService) {
|
||||
SDLHaptic haptic = getHaptic(deviceId_VIBRATOR_SERVICE);
|
||||
if (haptic == null) {
|
||||
haptic = new SDLHaptic();
|
||||
haptic.device_id = deviceId_VIBRATOR_SERVICE;
|
||||
haptic.name = "VIBRATOR_SERVICE";
|
||||
haptic.vib = vib;
|
||||
mHaptics.add(haptic);
|
||||
SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check removed devices */
|
||||
ArrayList<Integer> removedDevices = null;
|
||||
for (SDLHaptic haptic : mHaptics) {
|
||||
int device_id = haptic.device_id;
|
||||
if (device_id != deviceId_VIBRATOR_SERVICE || !hasVibratorService) {
|
||||
if (removedDevices == null) {
|
||||
removedDevices = new ArrayList<Integer>();
|
||||
}
|
||||
removedDevices.add(device_id);
|
||||
} // else: don't remove the vibrator if it is still present
|
||||
}
|
||||
|
||||
if (removedDevices != null) {
|
||||
for (int device_id : removedDevices) {
|
||||
SDLControllerManager.nativeRemoveHaptic(device_id);
|
||||
for (int i = 0; i < mHaptics.size(); i++) {
|
||||
if (mHaptics.get(i).device_id == device_id) {
|
||||
mHaptics.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected SDLHaptic getHaptic(int device_id) {
|
||||
for (SDLHaptic haptic : mHaptics) {
|
||||
if (haptic.device_id == device_id) {
|
||||
return haptic;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class SDLGenericMotionListener_API14 implements View.OnGenericMotionListener {
|
||||
protected static final int SDL_PEN_DEVICE_TYPE_UNKNOWN = 0;
|
||||
protected static final int SDL_PEN_DEVICE_TYPE_DIRECT = 1;
|
||||
protected static final int SDL_PEN_DEVICE_TYPE_INDIRECT = 2;
|
||||
|
||||
// Generic Motion (mouse hover, joystick...) events go here
|
||||
@Override
|
||||
public boolean onGenericMotion(View v, MotionEvent event) {
|
||||
if (event.getSource() == InputDevice.SOURCE_JOYSTICK)
|
||||
return SDLControllerManager.handleJoystickMotionEvent(event);
|
||||
|
||||
float x, y;
|
||||
int action = event.getActionMasked();
|
||||
int pointerCount = event.getPointerCount();
|
||||
boolean consumed = false;
|
||||
|
||||
for (int i = 0; i < pointerCount; i++) {
|
||||
int toolType = event.getToolType(i);
|
||||
|
||||
if (toolType == MotionEvent.TOOL_TYPE_MOUSE) {
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_SCROLL:
|
||||
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, i);
|
||||
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, i);
|
||||
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_HOVER_MOVE:
|
||||
x = getEventX(event, i);
|
||||
y = getEventY(event, i);
|
||||
|
||||
SDLActivity.onNativeMouse(0, action, x, y, checkRelativeEvent(event));
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (toolType == MotionEvent.TOOL_TYPE_STYLUS || toolType == MotionEvent.TOOL_TYPE_ERASER) {
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_HOVER_ENTER:
|
||||
case MotionEvent.ACTION_HOVER_MOVE:
|
||||
case MotionEvent.ACTION_HOVER_EXIT:
|
||||
x = event.getX(i);
|
||||
y = event.getY(i);
|
||||
float p = event.getPressure(i);
|
||||
if (p > 1.0f) {
|
||||
// may be larger than 1.0f on some devices
|
||||
// see the documentation of getPressure(i)
|
||||
p = 1.0f;
|
||||
}
|
||||
|
||||
// BUTTON_STYLUS_PRIMARY is 2^5, so shift by 4, and apply SDL_PEN_INPUT_DOWN/SDL_PEN_INPUT_ERASER_TIP
|
||||
int buttons = (event.getButtonState() >> 4) | (1 << (toolType == MotionEvent.TOOL_TYPE_STYLUS ? 0 : 30));
|
||||
|
||||
SDLActivity.onNativePen(event.getPointerId(i), getPenDeviceType(event.getDevice()), buttons, action, x, y, p);
|
||||
consumed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
boolean supportsRelativeMouse() {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean inRelativeMode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean setRelativeMouseEnabled(boolean enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void reclaimRelativeMouseModeIfNeeded() {
|
||||
|
||||
}
|
||||
|
||||
boolean checkRelativeEvent(MotionEvent event) {
|
||||
return inRelativeMode();
|
||||
}
|
||||
|
||||
float getEventX(MotionEvent event, int pointerIndex) {
|
||||
return event.getX(pointerIndex);
|
||||
}
|
||||
|
||||
float getEventY(MotionEvent event, int pointerIndex) {
|
||||
return event.getY(pointerIndex);
|
||||
}
|
||||
|
||||
int getPenDeviceType(InputDevice penDevice) {
|
||||
return SDL_PEN_DEVICE_TYPE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
class SDLGenericMotionListener_API24 extends SDLGenericMotionListener_API14 {
|
||||
// Generic Motion (mouse hover, joystick...) events go here
|
||||
|
||||
private boolean mRelativeModeEnabled;
|
||||
|
||||
@Override
|
||||
boolean supportsRelativeMouse() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean inRelativeMode() {
|
||||
return mRelativeModeEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean setRelativeMouseEnabled(boolean enabled) {
|
||||
mRelativeModeEnabled = enabled;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
float getEventX(MotionEvent event, int pointerIndex) {
|
||||
if (Build.VERSION.SDK_INT < 24 /* Android 7.0 (N) */) {
|
||||
/* Silence 'lint' warning */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mRelativeModeEnabled && event.getToolType(pointerIndex) == MotionEvent.TOOL_TYPE_MOUSE) {
|
||||
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_X, pointerIndex);
|
||||
} else {
|
||||
return event.getX(pointerIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
float getEventY(MotionEvent event, int pointerIndex) {
|
||||
if (Build.VERSION.SDK_INT < 24 /* Android 7.0 (N) */) {
|
||||
/* Silence 'lint' warning */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mRelativeModeEnabled && event.getToolType(pointerIndex) == MotionEvent.TOOL_TYPE_MOUSE) {
|
||||
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y, pointerIndex);
|
||||
} else {
|
||||
return event.getY(pointerIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 {
|
||||
// Generic Motion (mouse hover, joystick...) events go here
|
||||
private boolean mRelativeModeEnabled;
|
||||
|
||||
@Override
|
||||
boolean supportsRelativeMouse() {
|
||||
return (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean inRelativeMode() {
|
||||
return mRelativeModeEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean setRelativeMouseEnabled(boolean enabled) {
|
||||
|
||||
if (Build.VERSION.SDK_INT < 26 /* Android 8.0 (O) */) {
|
||||
/* Silence 'lint' warning */
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */) {
|
||||
if (enabled) {
|
||||
SDLActivity.getContentView().requestPointerCapture();
|
||||
} else {
|
||||
SDLActivity.getContentView().releasePointerCapture();
|
||||
}
|
||||
mRelativeModeEnabled = enabled;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void reclaimRelativeMouseModeIfNeeded() {
|
||||
|
||||
if (Build.VERSION.SDK_INT < 26 /* Android 8.0 (O) */) {
|
||||
/* Silence 'lint' warning */
|
||||
return;
|
||||
}
|
||||
|
||||
if (mRelativeModeEnabled && !SDLActivity.isDeXMode()) {
|
||||
SDLActivity.getContentView().requestPointerCapture();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean checkRelativeEvent(MotionEvent event) {
|
||||
if (Build.VERSION.SDK_INT < 26 /* Android 8.0 (O) */) {
|
||||
/* Silence 'lint' warning */
|
||||
return false;
|
||||
}
|
||||
return event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE;
|
||||
}
|
||||
|
||||
@Override
|
||||
float getEventX(MotionEvent event, int pointerIndex) {
|
||||
// Relative mouse in capture mode will only have relative for X/Y
|
||||
return event.getX(pointerIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
float getEventY(MotionEvent event, int pointerIndex) {
|
||||
// Relative mouse in capture mode will only have relative for X/Y
|
||||
return event.getY(pointerIndex);
|
||||
}
|
||||
}
|
||||
|
||||
class SDLGenericMotionListener_API29 extends SDLGenericMotionListener_API26 {
|
||||
@Override
|
||||
int getPenDeviceType(InputDevice penDevice)
|
||||
{
|
||||
if (penDevice == null) {
|
||||
return SDL_PEN_DEVICE_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
return penDevice.isExternal() ? SDL_PEN_DEVICE_TYPE_INDIRECT : SDL_PEN_DEVICE_TYPE_DIRECT;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
import android.content.*;
|
||||
import android.text.InputType;
|
||||
import android.view.*;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
|
||||
/* This is a fake invisible editor view that receives the input and defines the
|
||||
* pan&scan region
|
||||
*/
|
||||
public class SDLDummyEdit extends View implements View.OnKeyListener
|
||||
{
|
||||
InputConnection ic;
|
||||
int input_type;
|
||||
|
||||
SDLDummyEdit(Context context) {
|
||||
super(context);
|
||||
setFocusableInTouchMode(true);
|
||||
setFocusable(true);
|
||||
setOnKeyListener(this);
|
||||
}
|
||||
|
||||
void setInputType(int input_type) {
|
||||
this.input_type = input_type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCheckIsTextEditor() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
return SDLActivity.handleKeyEvent(v, keyCode, event, ic);
|
||||
}
|
||||
|
||||
//
|
||||
@Override
|
||||
public boolean onKeyPreIme (int keyCode, KeyEvent event) {
|
||||
// As seen on StackOverflow: http://stackoverflow.com/questions/7634346/keyboard-hide-event
|
||||
// FIXME: Discussion at http://bugzilla.libsdl.org/show_bug.cgi?id=1639
|
||||
// FIXME: This is not a 100% effective solution to the problem of detecting if the keyboard is showing or not
|
||||
// FIXME: A more effective solution would be to assume our Layout to be RelativeLayout or LinearLayout
|
||||
// FIXME: And determine the keyboard presence doing this: http://stackoverflow.com/questions/2150078/how-to-check-visibility-of-software-keyboard-in-android
|
||||
// FIXME: An even more effective way would be if Android provided this out of the box, but where would the fun be in that :)
|
||||
if (event.getAction()==KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
if (SDLActivity.mTextEdit != null && SDLActivity.mTextEdit.getVisibility() == View.VISIBLE) {
|
||||
SDLActivity.onNativeKeyboardFocusLost();
|
||||
}
|
||||
}
|
||||
return super.onKeyPreIme(keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
|
||||
ic = new SDLInputConnection(this, true);
|
||||
|
||||
outAttrs.inputType = input_type;
|
||||
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI |
|
||||
EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */;
|
||||
|
||||
return ic;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,138 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
import android.content.*;
|
||||
import android.os.Build;
|
||||
import android.text.Editable;
|
||||
import android.view.*;
|
||||
import android.view.inputmethod.BaseInputConnection;
|
||||
import android.widget.EditText;
|
||||
|
||||
class SDLInputConnection extends BaseInputConnection
|
||||
{
|
||||
protected EditText mEditText;
|
||||
protected String mCommittedText = "";
|
||||
|
||||
SDLInputConnection(View targetView, boolean fullEditor) {
|
||||
super(targetView, fullEditor);
|
||||
mEditText = new EditText(SDL.getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editable getEditable() {
|
||||
return mEditText.getEditableText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendKeyEvent(KeyEvent event) {
|
||||
/*
|
||||
* This used to handle the keycodes from soft keyboard (and IME-translated input from hardkeyboard)
|
||||
* However, as of Ice Cream Sandwich and later, almost all soft keyboard doesn't generate key presses
|
||||
* and so we need to generate them ourselves in commitText. To avoid duplicates on the handful of keys
|
||||
* that still do, we empty this out.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Return DOES still generate a key event, however. So rather than using it as the 'click a button' key
|
||||
* as we do with physical keyboards, let's just use it to hide the keyboard.
|
||||
*/
|
||||
|
||||
if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
|
||||
if (SDLActivity.onNativeSoftReturnKey()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return super.sendKeyEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean commitText(CharSequence text, int newCursorPosition) {
|
||||
if (!super.commitText(text, newCursorPosition)) {
|
||||
return false;
|
||||
}
|
||||
updateText();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setComposingText(CharSequence text, int newCursorPosition) {
|
||||
if (!super.setComposingText(text, newCursorPosition)) {
|
||||
return false;
|
||||
}
|
||||
updateText();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
|
||||
if (Build.VERSION.SDK_INT <= 29 /* Android 10.0 (Q) */) {
|
||||
// Workaround to capture backspace key. Ref: http://stackoverflow.com/questions>/14560344/android-backspace-in-webview-baseinputconnection
|
||||
// and https://bugzilla.libsdl.org/show_bug.cgi?id=2265
|
||||
if (beforeLength > 0 && afterLength == 0) {
|
||||
// backspace(s)
|
||||
while (beforeLength-- > 0) {
|
||||
nativeGenerateScancodeForUnichar('\b');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!super.deleteSurroundingText(beforeLength, afterLength)) {
|
||||
return false;
|
||||
}
|
||||
updateText();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void updateText() {
|
||||
final Editable content = getEditable();
|
||||
if (content == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String text = content.toString();
|
||||
int compareLength = Math.min(text.length(), mCommittedText.length());
|
||||
int matchLength, offset;
|
||||
|
||||
/* Backspace over characters that are no longer in the string */
|
||||
for (matchLength = 0; matchLength < compareLength; ) {
|
||||
int codePoint = mCommittedText.codePointAt(matchLength);
|
||||
if (codePoint != text.codePointAt(matchLength)) {
|
||||
break;
|
||||
}
|
||||
matchLength += Character.charCount(codePoint);
|
||||
}
|
||||
/* FIXME: This doesn't handle graphemes, like '🌬️' */
|
||||
for (offset = matchLength; offset < mCommittedText.length(); ) {
|
||||
int codePoint = mCommittedText.codePointAt(offset);
|
||||
nativeGenerateScancodeForUnichar('\b');
|
||||
offset += Character.charCount(codePoint);
|
||||
}
|
||||
|
||||
if (matchLength < text.length()) {
|
||||
String pendingText = text.subSequence(matchLength, text.length()).toString();
|
||||
if (!SDLActivity.dispatchingKeyEvent()) {
|
||||
for (offset = 0; offset < pendingText.length(); ) {
|
||||
int codePoint = pendingText.codePointAt(offset);
|
||||
if (codePoint == '\n') {
|
||||
if (SDLActivity.onNativeSoftReturnKey()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Higher code points don't generate simulated scancodes */
|
||||
if (codePoint > 0 && codePoint < 128) {
|
||||
nativeGenerateScancodeForUnichar((char)codePoint);
|
||||
}
|
||||
offset += Character.charCount(codePoint);
|
||||
}
|
||||
}
|
||||
SDLInputConnection.nativeCommitText(pendingText, 0);
|
||||
}
|
||||
mCommittedText = text;
|
||||
}
|
||||
|
||||
public static native void nativeCommitText(String text, int newCursorPosition);
|
||||
|
||||
public static native void nativeGenerateScancodeForUnichar(char c);
|
||||
}
|
||||
|
||||
@ -0,0 +1,446 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.graphics.Insets;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.os.Build;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.PointerIcon;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import android.view.ScaleGestureDetector;
|
||||
|
||||
/**
|
||||
SDLSurface. This is what we draw on, so we need to know when it's created
|
||||
in order to do anything useful.
|
||||
|
||||
Because of this, that's where we set up the SDL thread
|
||||
*/
|
||||
public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
|
||||
View.OnApplyWindowInsetsListener, View.OnKeyListener, View.OnTouchListener,
|
||||
SensorEventListener, ScaleGestureDetector.OnScaleGestureListener {
|
||||
|
||||
// Sensors
|
||||
protected SensorManager mSensorManager;
|
||||
protected Display mDisplay;
|
||||
|
||||
// Keep track of the surface size to normalize touch events
|
||||
protected float mWidth, mHeight;
|
||||
|
||||
// Is SurfaceView ready for rendering
|
||||
protected boolean mIsSurfaceReady;
|
||||
|
||||
// Pinch events
|
||||
private final ScaleGestureDetector scaleGestureDetector;
|
||||
|
||||
// Startup
|
||||
protected SDLSurface(Context context) {
|
||||
super(context);
|
||||
getHolder().addCallback(this);
|
||||
|
||||
scaleGestureDetector = new ScaleGestureDetector(context, this);
|
||||
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
requestFocus();
|
||||
setOnApplyWindowInsetsListener(this);
|
||||
setOnKeyListener(this);
|
||||
setOnTouchListener(this);
|
||||
|
||||
mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
|
||||
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
|
||||
|
||||
setOnGenericMotionListener(SDLActivity.getMotionListener());
|
||||
|
||||
// Some arbitrary defaults to avoid a potential division by zero
|
||||
mWidth = 1.0f;
|
||||
mHeight = 1.0f;
|
||||
|
||||
mIsSurfaceReady = false;
|
||||
}
|
||||
|
||||
protected void handlePause() {
|
||||
enableSensor(Sensor.TYPE_ACCELEROMETER, false);
|
||||
}
|
||||
|
||||
protected void handleResume() {
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
requestFocus();
|
||||
setOnApplyWindowInsetsListener(this);
|
||||
setOnKeyListener(this);
|
||||
setOnTouchListener(this);
|
||||
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
|
||||
}
|
||||
|
||||
protected Surface getNativeSurface() {
|
||||
return getHolder().getSurface();
|
||||
}
|
||||
|
||||
// Called when we have a valid drawing surface
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
Log.v("SDL", "surfaceCreated()");
|
||||
SDLActivity.onNativeSurfaceCreated();
|
||||
}
|
||||
|
||||
// Called when we lose the surface
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
Log.v("SDL", "surfaceDestroyed()");
|
||||
|
||||
// Transition to pause, if needed
|
||||
SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED;
|
||||
SDLActivity.handleNativeState();
|
||||
|
||||
mIsSurfaceReady = false;
|
||||
SDLActivity.onNativeSurfaceDestroyed();
|
||||
}
|
||||
|
||||
// Called when the surface is resized
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder,
|
||||
int format, int width, int height) {
|
||||
Log.v("SDL", "surfaceChanged()");
|
||||
|
||||
if (SDLActivity.mSingleton == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
int nDeviceWidth = width;
|
||||
int nDeviceHeight = height;
|
||||
float density = 1.0f;
|
||||
try
|
||||
{
|
||||
DisplayMetrics realMetrics = new DisplayMetrics();
|
||||
mDisplay.getRealMetrics( realMetrics );
|
||||
nDeviceWidth = realMetrics.widthPixels;
|
||||
nDeviceHeight = realMetrics.heightPixels;
|
||||
// Use densityDpi instead of density to more closely match what the UI scale is
|
||||
density = (float)realMetrics.densityDpi / 160.0f;
|
||||
} catch(Exception ignored) {
|
||||
}
|
||||
|
||||
synchronized(SDLActivity.getContext()) {
|
||||
// In case we're waiting on a size change after going fullscreen, send a notification.
|
||||
SDLActivity.getContext().notifyAll();
|
||||
}
|
||||
|
||||
Log.v("SDL", "Window size: " + width + "x" + height);
|
||||
Log.v("SDL", "Device size: " + nDeviceWidth + "x" + nDeviceHeight);
|
||||
SDLActivity.nativeSetScreenResolution(width, height, nDeviceWidth, nDeviceHeight, density, mDisplay.getRefreshRate());
|
||||
SDLActivity.onNativeResize();
|
||||
|
||||
// Prevent a screen distortion glitch,
|
||||
// for instance when the device is in Landscape and a Portrait App is resumed.
|
||||
boolean skip = false;
|
||||
int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation();
|
||||
|
||||
if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
|
||||
if (mWidth > mHeight) {
|
||||
skip = true;
|
||||
}
|
||||
} else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
|
||||
if (mWidth < mHeight) {
|
||||
skip = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Special Patch for Square Resolution: Black Berry Passport
|
||||
if (skip) {
|
||||
double min = Math.min(mWidth, mHeight);
|
||||
double max = Math.max(mWidth, mHeight);
|
||||
|
||||
if (max / min < 1.20) {
|
||||
Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution.");
|
||||
skip = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't skip if we might be multi-window or have popup dialogs
|
||||
if (skip) {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
skip = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (skip) {
|
||||
Log.v("SDL", "Skip .. Surface is not ready.");
|
||||
mIsSurfaceReady = false;
|
||||
return;
|
||||
}
|
||||
|
||||
/* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
|
||||
SDLActivity.onNativeSurfaceChanged();
|
||||
|
||||
/* Surface is ready */
|
||||
mIsSurfaceReady = true;
|
||||
|
||||
SDLActivity.mNextNativeState = SDLActivity.NativeState.RESUMED;
|
||||
SDLActivity.handleNativeState();
|
||||
}
|
||||
|
||||
// Window inset
|
||||
@Override
|
||||
public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
|
||||
if (Build.VERSION.SDK_INT >= 30 /* Android 11 (R) */) {
|
||||
Insets combined = insets.getInsets(WindowInsets.Type.systemBars() |
|
||||
WindowInsets.Type.systemGestures() |
|
||||
WindowInsets.Type.mandatorySystemGestures() |
|
||||
WindowInsets.Type.tappableElement() |
|
||||
WindowInsets.Type.displayCutout());
|
||||
|
||||
SDLActivity.onNativeInsetsChanged(combined.left, combined.right, combined.top, combined.bottom);
|
||||
}
|
||||
|
||||
// Pass these to any child views in case they need them
|
||||
return insets;
|
||||
}
|
||||
|
||||
// Key events
|
||||
@Override
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
return SDLActivity.handleKeyEvent(v, keyCode, event, null);
|
||||
}
|
||||
|
||||
private float getNormalizedX(float x)
|
||||
{
|
||||
if (mWidth <= 1) {
|
||||
return 0.5f;
|
||||
} else {
|
||||
return (x / (mWidth - 1));
|
||||
}
|
||||
}
|
||||
|
||||
private float getNormalizedY(float y)
|
||||
{
|
||||
if (mHeight <= 1) {
|
||||
return 0.5f;
|
||||
} else {
|
||||
return (y / (mHeight - 1));
|
||||
}
|
||||
}
|
||||
|
||||
// Touch events
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
/* Ref: http://developer.android.com/training/gestures/multi.html */
|
||||
int touchDevId = event.getDeviceId();
|
||||
final int pointerCount = event.getPointerCount();
|
||||
int action = event.getActionMasked();
|
||||
int pointerId;
|
||||
int i = 0;
|
||||
float x,y,p;
|
||||
|
||||
if (action == MotionEvent.ACTION_POINTER_UP || action == MotionEvent.ACTION_POINTER_DOWN)
|
||||
i = event.getActionIndex();
|
||||
|
||||
do {
|
||||
int toolType = event.getToolType(i);
|
||||
|
||||
if (toolType == MotionEvent.TOOL_TYPE_MOUSE) {
|
||||
int buttonState = event.getButtonState();
|
||||
boolean relative = false;
|
||||
|
||||
// We need to check if we're in relative mouse mode and get the axis offset rather than the x/y values
|
||||
// if we are. We'll leverage our existing mouse motion listener
|
||||
SDLGenericMotionListener_API14 motionListener = SDLActivity.getMotionListener();
|
||||
x = motionListener.getEventX(event, i);
|
||||
y = motionListener.getEventY(event, i);
|
||||
relative = motionListener.inRelativeMode();
|
||||
|
||||
SDLActivity.onNativeMouse(buttonState, action, x, y, relative);
|
||||
} else if (toolType == MotionEvent.TOOL_TYPE_STYLUS || toolType == MotionEvent.TOOL_TYPE_ERASER) {
|
||||
pointerId = event.getPointerId(i);
|
||||
x = event.getX(i);
|
||||
y = event.getY(i);
|
||||
p = event.getPressure(i);
|
||||
if (p > 1.0f) {
|
||||
// may be larger than 1.0f on some devices
|
||||
// see the documentation of getPressure(i)
|
||||
p = 1.0f;
|
||||
}
|
||||
|
||||
// BUTTON_STYLUS_PRIMARY is 2^5, so shift by 4, and apply SDL_PEN_INPUT_DOWN/SDL_PEN_INPUT_ERASER_TIP
|
||||
int buttonState = (event.getButtonState() >> 4) | (1 << (toolType == MotionEvent.TOOL_TYPE_STYLUS ? 0 : 30));
|
||||
|
||||
SDLActivity.onNativePen(pointerId, SDLActivity.getMotionListener().getPenDeviceType(event.getDevice()), buttonState, action, x, y, p);
|
||||
} else { // MotionEvent.TOOL_TYPE_FINGER or MotionEvent.TOOL_TYPE_UNKNOWN
|
||||
pointerId = event.getPointerId(i);
|
||||
x = getNormalizedX(event.getX(i));
|
||||
y = getNormalizedY(event.getY(i));
|
||||
p = event.getPressure(i);
|
||||
if (p > 1.0f) {
|
||||
// may be larger than 1.0f on some devices
|
||||
// see the documentation of getPressure(i)
|
||||
p = 1.0f;
|
||||
}
|
||||
|
||||
SDLActivity.onNativeTouch(touchDevId, pointerId, action, x, y, p);
|
||||
}
|
||||
|
||||
// Non-primary up/down
|
||||
if (action == MotionEvent.ACTION_POINTER_UP || action == MotionEvent.ACTION_POINTER_DOWN)
|
||||
break;
|
||||
} while (++i < pointerCount);
|
||||
|
||||
scaleGestureDetector.onTouchEvent(event);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sensor events
|
||||
protected void enableSensor(int sensortype, boolean enabled) {
|
||||
// TODO: This uses getDefaultSensor - what if we have >1 accels?
|
||||
if (enabled) {
|
||||
mSensorManager.registerListener(this,
|
||||
mSensorManager.getDefaultSensor(sensortype),
|
||||
SensorManager.SENSOR_DELAY_GAME, null);
|
||||
} else {
|
||||
mSensorManager.unregisterListener(this,
|
||||
mSensorManager.getDefaultSensor(sensortype));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
|
||||
|
||||
// Since we may have an orientation set, we won't receive onConfigurationChanged events.
|
||||
// We thus should check here.
|
||||
int newRotation;
|
||||
|
||||
float x, y;
|
||||
switch (mDisplay.getRotation()) {
|
||||
case Surface.ROTATION_0:
|
||||
default:
|
||||
x = event.values[0];
|
||||
y = event.values[1];
|
||||
newRotation = 0;
|
||||
break;
|
||||
case Surface.ROTATION_90:
|
||||
x = -event.values[1];
|
||||
y = event.values[0];
|
||||
newRotation = 90;
|
||||
break;
|
||||
case Surface.ROTATION_180:
|
||||
x = -event.values[0];
|
||||
y = -event.values[1];
|
||||
newRotation = 180;
|
||||
break;
|
||||
case Surface.ROTATION_270:
|
||||
x = event.values[1];
|
||||
y = -event.values[0];
|
||||
newRotation = 270;
|
||||
break;
|
||||
}
|
||||
|
||||
if (newRotation != SDLActivity.mCurrentRotation) {
|
||||
SDLActivity.mCurrentRotation = newRotation;
|
||||
SDLActivity.onNativeRotationChanged(newRotation);
|
||||
}
|
||||
|
||||
SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
|
||||
y / SensorManager.GRAVITY_EARTH,
|
||||
event.values[2] / SensorManager.GRAVITY_EARTH);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent android internal NullPointerException (https://github.com/libsdl-org/SDL/issues/13306)
|
||||
@Override
|
||||
public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) {
|
||||
try {
|
||||
return super.onResolvePointerIcon(event, pointerIndex);
|
||||
} catch (NullPointerException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Captured pointer events for API 26.
|
||||
@Override
|
||||
public boolean onCapturedPointerEvent(MotionEvent event)
|
||||
{
|
||||
int action = event.getActionMasked();
|
||||
int pointerCount = event.getPointerCount();
|
||||
|
||||
for (int i = 0; i < pointerCount; i++) {
|
||||
float x, y;
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_SCROLL:
|
||||
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, i);
|
||||
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, i);
|
||||
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_HOVER_MOVE:
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
x = event.getX(i);
|
||||
y = event.getY(i);
|
||||
SDLActivity.onNativeMouse(0, action, x, y, true);
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_BUTTON_PRESS:
|
||||
case MotionEvent.ACTION_BUTTON_RELEASE:
|
||||
|
||||
// Change our action value to what SDL's code expects.
|
||||
if (action == MotionEvent.ACTION_BUTTON_PRESS) {
|
||||
action = MotionEvent.ACTION_DOWN;
|
||||
} else { /* MotionEvent.ACTION_BUTTON_RELEASE */
|
||||
action = MotionEvent.ACTION_UP;
|
||||
}
|
||||
|
||||
x = event.getX(i);
|
||||
y = event.getY(i);
|
||||
int button = event.getButtonState();
|
||||
|
||||
SDLActivity.onNativeMouse(button, action, x, y, true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScale(ScaleGestureDetector detector) {
|
||||
float scale = detector.getScaleFactor();
|
||||
SDLActivity.onNativePinchUpdate(scale);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScaleBegin(ScaleGestureDetector detector) {
|
||||
SDLActivity.onNativePinchStart();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScaleEnd(ScaleGestureDetector detector) {
|
||||
SDLActivity.onNativePinchEnd();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 916 B |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
10
Android/AndroidProject/app/src/main/res/values/colors.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="purple_200">#FFBB86FC</color>
|
||||
<color name="purple_500">#FF6200EE</color>
|
||||
<color name="purple_700">#FF3700B3</color>
|
||||
<color name="teal_200">#FF03DAC5</color>
|
||||
<color name="teal_700">#FF018786</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
</resources>
|
||||
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#000000</color>
|
||||
</resources>
|
||||
@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">IAEGame</string>
|
||||
</resources>
|
||||
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Theme.IAEGame" parent="android:Theme.Material.Light.NoActionBar" />
|
||||
</resources>
|
||||
13
Android/AndroidProject/app/src/main/res/xml/backup_rules.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Sample backup rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/guide/topics/data/autobackup
|
||||
for details.
|
||||
Note: This file is ignored for devices older than API 31
|
||||
See https://developer.android.com/about/versions/12/backup-restore
|
||||
-->
|
||||
<full-backup-content>
|
||||
<!--
|
||||
<include domain="sharedpref" path="."/>
|
||||
<exclude domain="sharedpref" path="device.xml"/>
|
||||
-->
|
||||
</full-backup-content>
|
||||
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Sample data extraction rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
|
||||
for details.
|
||||
-->
|
||||
<data-extraction-rules>
|
||||
<cloud-backup>
|
||||
<!-- TODO: Use <include> and <exclude> to control what is backed up.
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
-->
|
||||
</cloud-backup>
|
||||
<!--
|
||||
<device-transfer>
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
</device-transfer>
|
||||
-->
|
||||
</data-extraction-rules>
|
||||
6
Android/AndroidProject/build.gradle.kts
Normal file
@ -0,0 +1,6 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
alias(libs.plugins.android.application) apply false
|
||||
alias(libs.plugins.kotlin.android) apply false
|
||||
alias(libs.plugins.kotlin.compose) apply false
|
||||
}
|
||||
23
Android/AndroidProject/gradle.properties
Normal file
@ -0,0 +1,23 @@
|
||||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. For more details, visit
|
||||
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
# Enables namespacing of each library's R class so that its R class includes only the
|
||||
# resources declared in the library itself and none from the library's dependencies,
|
||||
# thereby reducing the size of the R class for that library
|
||||
android.nonTransitiveRClass=true
|
||||
32
Android/AndroidProject/gradle/libs.versions.toml
Normal file
@ -0,0 +1,32 @@
|
||||
[versions]
|
||||
agp = "8.13.0"
|
||||
kotlin = "2.0.21"
|
||||
coreKtx = "1.17.0"
|
||||
junit = "4.13.2"
|
||||
junitVersion = "1.3.0"
|
||||
espressoCore = "3.7.0"
|
||||
lifecycleRuntimeKtx = "2.9.4"
|
||||
activityCompose = "1.11.0"
|
||||
composeBom = "2024.09.00"
|
||||
|
||||
[libraries]
|
||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
||||
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
|
||||
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
|
||||
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
||||
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
|
||||
androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
|
||||
androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
|
||||
androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
|
||||
androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
||||
androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
||||
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||
|
||||
BIN
Android/AndroidProject/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
6
Android/AndroidProject/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
#Fri Oct 17 23:05:59 IST 2025
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
185
Android/AndroidProject/gradlew
vendored
Normal file
@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
89
Android/AndroidProject/gradlew.bat
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
23
Android/AndroidProject/settings.gradle.kts
Normal file
@ -0,0 +1,23 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
google {
|
||||
content {
|
||||
includeGroupByRegex("com\\.android.*")
|
||||
includeGroupByRegex("com\\.google.*")
|
||||
includeGroupByRegex("androidx.*")
|
||||
}
|
||||
}
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "IAEGame"
|
||||
include(":app")
|
||||
150
Android/android-ndk-r27d/CHANGELOG.md
Normal file
@ -0,0 +1,150 @@
|
||||
# Changelog
|
||||
|
||||
Report issues to [GitHub].
|
||||
|
||||
For Android Studio issues, go to https://b.android.com and file a bug using the
|
||||
Android Studio component, not the NDK component.
|
||||
|
||||
If you're a build system maintainer that needs to use the tools in the NDK
|
||||
directly, see the [build system maintainers guide].
|
||||
|
||||
[GitHub]: https://github.com/android/ndk/issues
|
||||
[build system maintainers guide]: https://android.googlesource.com/platform/ndk/+/master/docs/BuildSystemMaintainers.md
|
||||
|
||||
## Announcements
|
||||
|
||||
* Android V will allow OEMs to ship arm64-v8a devices with 16KiB page sizes.
|
||||
Devices that use this configuration will not be able to run existing apps that
|
||||
use native code. To be compatible with these devices, applications will need
|
||||
to rebuild all their native code to be 16KiB aligned, and rewrite any code
|
||||
which assumes a specific page size. ndk-build and CMake have options to enable
|
||||
this mode (see note about `APP_SUPPORT_FLEXIBLE_PAGE_SIZES` and
|
||||
`ANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES` below). A future version of the NDK will
|
||||
enable this mode by default. If you're using or maintaining a third-party
|
||||
build system, consult the [build system maintainers guide] for instructions.
|
||||
|
||||
See [Support 16 KB page sizes] for more information.
|
||||
|
||||
[Support 16 KB page sizes]: https://developer.android.com/guide/practices/page-sizes
|
||||
|
||||
## r27d
|
||||
|
||||
* Updated LLVM to clang-r522817d. See `clang_source_info.md` in the toolchain
|
||||
directory for version information.
|
||||
* [Issue 2144]: Fixed issue where `lldb.sh` did not work when installed to a
|
||||
path which contained spaces.
|
||||
* [Issue 2143]: Fixed lldb.sh not finding libpython on macOS.
|
||||
|
||||
[Issue 2143]: https://github.com/android/ndk/issues/2143
|
||||
[Issue 2144]: https://github.com/android/ndk/issues/2144
|
||||
|
||||
## r27c
|
||||
|
||||
* Updated LLVM to clang-r522817c. See `clang_source_info.md` in the toolchain
|
||||
directory for version information.
|
||||
* [Issue 2040]: Further fixes for miscompiles of indirect gotos.
|
||||
* [Issue 2064]: Fix HWAsan miscompilation.
|
||||
* [Issue 2070]: Fix crash in instantiation of function definitions.
|
||||
* [Issue 2084]: Fix for incorrect compiler error on overloaded member
|
||||
functions with ref-qualifiers.
|
||||
|
||||
[Issue 2040]: https://github.com/android/ndk/issues/2040
|
||||
[Issue 2064]: https://github.com/android/ndk/issues/2064
|
||||
[Issue 2070]: https://github.com/android/ndk/issues/2070
|
||||
[Issue 2084]: https://github.com/android/ndk/issues/2084
|
||||
|
||||
## r27b
|
||||
|
||||
* Updated LLVM to clang-r522817b. See `clang_source_info.md` in the toolchain
|
||||
directory for version information.
|
||||
* [Issue 2040]: Fixed miscompile with indirect gotos.
|
||||
* [Issue 2054]: Fixed crash in x86 fp16 instruction selection.
|
||||
* [Issue 2059]: Fixed infinite loop in aarch64 vector instruction selection.
|
||||
* [Issue 2032]: Fixed compatibility issues with projects that used a very old
|
||||
`cmake_minimum_required` version ("Policy CMP0057 is not set: Support new
|
||||
IN_LIST if() operator").
|
||||
* [Issue 2039]: Fixed `LOCAL_STRIP_MODE` not being reset by
|
||||
`include $(CLEAR_VARS)`.
|
||||
* [Issue 2049]: Restored metadata used by CMake in non-toolchain-file use cases
|
||||
that used `CMAKE_SYSTEM_PROCESSOR` instead of `CMAKE_ANDROID_ARCH_ABI`. If you
|
||||
ran into this problem, you should switch to using `CMAKE_ANDROID_ARCH_ABI`
|
||||
because that's what [CMake's docs] say to use. Better still, use the NDK's
|
||||
[toolchain file].
|
||||
|
||||
[Issue 2032]: https://github.com/android/ndk/issues/2032
|
||||
[Issue 2039]: https://github.com/android/ndk/issues/2039
|
||||
[Issue 2040]: https://github.com/android/ndk/issues/2040
|
||||
[Issue 2049]: https://github.com/android/ndk/issues/2049
|
||||
[Issue 2054]: https://github.com/android/ndk/issues/2054
|
||||
[Issue 2059]: https://github.com/android/ndk/issues/2059
|
||||
[CMake's docs]: https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-android-with-the-ndk
|
||||
[toolchain file]: https://developer.android.com/ndk/guides/cmake
|
||||
|
||||
## Changes
|
||||
|
||||
* Updated LLVM to clang-r522817. See `clang_source_info.md` in the toolchain
|
||||
directory for version information.
|
||||
* [Issue 1728]: Clang now emits an error for invalid Android target versions.
|
||||
* [Issue 1853]: `clang-scan-deps` is now included.
|
||||
* [Issue 1911]: Fixed inconsistent `std::regex_replace` results.
|
||||
* [Issue 1947]: Improved support for AArch64 function multi-versioning in clang.
|
||||
* [Issue 1963]: Fixed undefined behavior in `std::unexpected::has_value()`.
|
||||
* [Issue 1988]: Added aarch64 support for `preserve_all` calling convention.
|
||||
* [Issue 2007]: Fixed crash in class template argument deduction caused by
|
||||
self-referential friend declaration.
|
||||
* [Issue 2010]: Removed superfluous libraries to reduce disk use.
|
||||
* [Issue 2012]: Fixed front end crash when using concepts and modules.
|
||||
* [Issue 2013]: Fixed false positive ODR violation in global module fragments.
|
||||
* [Issue 2023]: Fixed Clang crashes related to lambda captures in unevaluated
|
||||
contexts.
|
||||
* [Issue 2024]: Removed invalid `__attribute__((__const__))` from `gettid`.
|
||||
* A RISC-V sysroot (AKA riscv64, or rv64) has been added. It is **not**
|
||||
supported. It is present to aid bringup for OS vendors, but it's not yet a
|
||||
supported Android ABI. It will not be built by default.
|
||||
* [Issue 1856]: Target-prefixed cmd wrappers for clang should now behave
|
||||
appropriately when the first argument includes quotes. **You probably do not
|
||||
need to use those wrappers.** In most cases where you would use
|
||||
`aarch64-linux-android21-clang`, you can instead use `clang -target
|
||||
aarch64-linux-android21`, e.g. `CC="clang -target aarch64-linux-android21"
|
||||
./configure`. The wrappers are only needed when working with systems that do
|
||||
not properly handle a `CC` that includes arguments.
|
||||
* [Issue 1898]: ndk-stack now tolerates 0x prefixed addresses.
|
||||
* [Issue 1921]: `ANDROID_USE_LEGACY_TOOLCHAIN_FILE` value is now preserved
|
||||
during try-compile steps when `ON`.
|
||||
* [Issue 1974]: Unintentionally shipped Vulkan headers have been removed from
|
||||
`sources/third_party/vulkan`. The standard Vulkan headers are included in the
|
||||
Android sysroot, which Clang will find automatically.
|
||||
* [Issue 1993]: ndk-stack now tolerates invalid UTF-8 characters in the trace.
|
||||
* [Issue 1994]: Fixed ndk-gdb/ndk-lldb to use the correct path for
|
||||
make and other tools.
|
||||
* Added `APP_SUPPORT_FLEXIBLE_PAGE_SIZES` for ndk-build and
|
||||
`ANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES` for CMake. Set to
|
||||
`APP_SUPPORT_FLEXIBLE_PAGE_SIZES := true` in your `Application.mk` or pass
|
||||
`-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON` to CMake (via
|
||||
`android.defaultConfig.externalNativeBuild.cmake.arguments` if you're using
|
||||
the Android Gradle Plugin) to build your code to be compatible with devices
|
||||
that use a 16KiB page size. Third-party build system users and maintainers
|
||||
should consult the [build system maintainers guide].
|
||||
* Symlinks are now properly preserved in the macOS App Bundle. The NDK installed
|
||||
via that method is now the same size as the one installed via the SDK manager.
|
||||
* The unsupported libclang, libclang-cpp, libLLVM, and libLTO libraries were
|
||||
removed to save space.
|
||||
|
||||
[Issue 1728]: https://github.com/android/ndk/issues/1728
|
||||
[Issue 1853]: https://github.com/android/ndk/issues/1853
|
||||
[Issue 1856]: https://github.com/android/ndk/issues/1856
|
||||
[Issue 1898]: https://github.com/android/ndk/issues/1898
|
||||
[Issue 1911]: https://github.com/android/ndk/issues/1911
|
||||
[Issue 1921]: https://github.com/android/ndk/issues/1921
|
||||
[Issue 1947]: https://github.com/android/ndk/issues/1947
|
||||
[Issue 1963]: https://github.com/android/ndk/issues/1963
|
||||
[Issue 1974]: https://github.com/android/ndk/issues/1974
|
||||
[Issue 1988]: https://github.com/android/ndk/issues/1988
|
||||
[Issue 1993]: https://github.com/android/ndk/issues/1993
|
||||
[Issue 1994]: https://github.com/android/ndk/issues/1994
|
||||
[Issue 2007]: https://github.com/android/ndk/issues/2007
|
||||
[Issue 2010]: https://github.com/android/ndk/issues/2010
|
||||
[Issue 2012]: https://github.com/android/ndk/issues/2012
|
||||
[Issue 2013]: https://github.com/android/ndk/issues/2013
|
||||
[Issue 2023]: https://github.com/android/ndk/issues/2023
|
||||
[Issue 2024]: https://github.com/android/ndk/issues/2024
|
||||
9997
Android/android-ndk-r27d/NOTICE
Normal file
15302
Android/android-ndk-r27d/NOTICE.toolchain
Normal file
24
Android/android-ndk-r27d/README.md
Normal file
@ -0,0 +1,24 @@
|
||||
Android NDK
|
||||
===========
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
NDK documentation, guides, and API reference are available on
|
||||
[our website](https://developer.android.com/ndk/index.html).
|
||||
|
||||
NDK code samples are available on
|
||||
[GitHub](https://github.com/googlesamples/android-ndk).
|
||||
|
||||
Information about Android Studio can be found on [the Android Studio
|
||||
website](https://developer.android.com/studio/index.html).
|
||||
|
||||
Filing Bugs
|
||||
-----------
|
||||
|
||||
NDK bugs should be filed on
|
||||
[GitHub](https://github.com/android-ndk/ndk/issues/new).
|
||||
|
||||
Android Studio and Gradle bugs should be filed in the [Android Studio Bug
|
||||
Tracker](http://b.android.com). For the fastest response, make sure you follow
|
||||
their guide on [Filing Bugs](http://tools.android.com/filing-bugs).
|
||||
0
Android/android-ndk-r27d/build/__init__.py
Normal file
40
Android/android-ndk-r27d/build/cmake/abis.cmake
Normal file
@ -0,0 +1,40 @@
|
||||
set(NDK_DEFAULT_ABIS "arm64-v8a;armeabi-v7a;x86;x86_64")
|
||||
set(NDK_DEPRECATED_ABIS "")
|
||||
set(NDK_KNOWN_DEVICE_ABI32S "armeabi-v7a;x86")
|
||||
set(NDK_KNOWN_DEVICE_ABI64S "arm64-v8a;riscv64;x86_64")
|
||||
set(NDK_KNOWN_DEVICE_ABIS "arm64-v8a;armeabi-v7a;riscv64;x86;x86_64")
|
||||
set(NDK_ABI_armeabi-v7a_PROC "armv7-a")
|
||||
set(NDK_ABI_armeabi-v7a_ARCH "arm")
|
||||
set(NDK_ABI_armeabi-v7a_TRIPLE "arm-linux-androideabi")
|
||||
set(NDK_ABI_armeabi-v7a_LLVM_TRIPLE "armv7-none-linux-androideabi")
|
||||
set(NDK_ABI_armeabi-v7a_MIN_OS_VERSION "21")
|
||||
set(NDK_PROC_armv7-a_ABI "armeabi-v7a")
|
||||
set(NDK_ARCH_arm_ABI "armeabi-v7a")
|
||||
set(NDK_ABI_arm64-v8a_PROC "aarch64")
|
||||
set(NDK_ABI_arm64-v8a_ARCH "arm64")
|
||||
set(NDK_ABI_arm64-v8a_TRIPLE "aarch64-linux-android")
|
||||
set(NDK_ABI_arm64-v8a_LLVM_TRIPLE "aarch64-none-linux-android")
|
||||
set(NDK_ABI_arm64-v8a_MIN_OS_VERSION "21")
|
||||
set(NDK_PROC_aarch64_ABI "arm64-v8a")
|
||||
set(NDK_ARCH_arm64_ABI "arm64-v8a")
|
||||
set(NDK_ABI_riscv64_PROC "riscv64")
|
||||
set(NDK_ABI_riscv64_ARCH "riscv64")
|
||||
set(NDK_ABI_riscv64_TRIPLE "riscv64-linux-android")
|
||||
set(NDK_ABI_riscv64_LLVM_TRIPLE "riscv64-none-linux-android")
|
||||
set(NDK_ABI_riscv64_MIN_OS_VERSION "35")
|
||||
set(NDK_PROC_riscv64_ABI "riscv64")
|
||||
set(NDK_ARCH_riscv64_ABI "riscv64")
|
||||
set(NDK_ABI_x86_PROC "i686")
|
||||
set(NDK_ABI_x86_ARCH "x86")
|
||||
set(NDK_ABI_x86_TRIPLE "i686-linux-android")
|
||||
set(NDK_ABI_x86_LLVM_TRIPLE "i686-none-linux-android")
|
||||
set(NDK_ABI_x86_MIN_OS_VERSION "21")
|
||||
set(NDK_PROC_i686_ABI "x86")
|
||||
set(NDK_ARCH_x86_ABI "x86")
|
||||
set(NDK_ABI_x86_64_PROC "x86_64")
|
||||
set(NDK_ABI_x86_64_ARCH "x86_64")
|
||||
set(NDK_ABI_x86_64_TRIPLE "x86_64-linux-android")
|
||||
set(NDK_ABI_x86_64_LLVM_TRIPLE "x86_64-none-linux-android")
|
||||
set(NDK_ABI_x86_64_MIN_OS_VERSION "21")
|
||||
set(NDK_PROC_x86_64_ABI "x86_64")
|
||||
set(NDK_ARCH_x86_64_ABI "x86_64")
|
||||
64
Android/android-ndk-r27d/build/cmake/adjust_api_level.cmake
Normal file
@ -0,0 +1,64 @@
|
||||
include(${CMAKE_ANDROID_NDK}/build/cmake/abis.cmake)
|
||||
include(${CMAKE_ANDROID_NDK}/build/cmake/platforms.cmake)
|
||||
|
||||
function(adjust_api_level api_level result_name)
|
||||
# If no platform version was chosen by the user, default to the minimum
|
||||
# version supported by this NDK.
|
||||
if(NOT api_level)
|
||||
message(STATUS
|
||||
"ANDROID_PLATFORM not set. Defaulting to minimum supported version "
|
||||
"${NDK_MIN_PLATFORM_LEVEL}.")
|
||||
|
||||
set(api_level "android-${NDK_MIN_PLATFORM_LEVEL}")
|
||||
endif()
|
||||
|
||||
if(api_level STREQUAL "latest")
|
||||
message(STATUS
|
||||
"Using latest available ANDROID_PLATFORM: ${NDK_MAX_PLATFORM_LEVEL}.")
|
||||
set(api_level "android-${NDK_MAX_PLATFORM_LEVEL}")
|
||||
endif()
|
||||
|
||||
string(REPLACE "android-" "" result ${api_level})
|
||||
|
||||
# Aliases defined by meta/platforms.json include codename aliases for platform
|
||||
# API levels as well as cover any gaps in platforms that may not have had NDK
|
||||
# APIs.
|
||||
if(NOT "${NDK_PLATFORM_ALIAS_${result}}" STREQUAL "")
|
||||
message(STATUS
|
||||
"${api_level} is an alias for ${NDK_PLATFORM_ALIAS_${result}}. Adjusting "
|
||||
"ANDROID_PLATFORM to match.")
|
||||
set(api_level "${NDK_PLATFORM_ALIAS_${result}}")
|
||||
string(REPLACE "android-" "" result ${api_level})
|
||||
endif()
|
||||
|
||||
# Pull up to the minimum supported version if an old API level was requested.
|
||||
if(result LESS NDK_MIN_PLATFORM_LEVEL)
|
||||
message(STATUS
|
||||
"${api_level} is unsupported. Using minimum supported version "
|
||||
"${NDK_MIN_PLATFORM_LEVEL}.")
|
||||
set(api_level "android-${NDK_MIN_PLATFORM_LEVEL}")
|
||||
string(REPLACE "android-" "" result ${api_level})
|
||||
endif()
|
||||
|
||||
# Pull up any ABI-specific minimum API levels.
|
||||
set(min_for_abi ${NDK_ABI_${ANDROID_ABI}_MIN_OS_VERSION})
|
||||
|
||||
if(result LESS min_for_abi)
|
||||
message(STATUS
|
||||
"android-${result} is not supported for ${ANDROID_ABI}. Using minimum "
|
||||
"supported ${ANDROID_ABI} version ${min_for_abi}.")
|
||||
set(api_level android-${min_for_abi})
|
||||
set(result ${min_for_abi})
|
||||
endif()
|
||||
|
||||
# ANDROID_PLATFORM beyond the maximum is an error. The correct way to specify
|
||||
# the latest version is ANDROID_PLATFORM=latest.
|
||||
if(result GREATER NDK_MAX_PLATFORM_LEVEL)
|
||||
message(SEND_ERROR
|
||||
"${api_level} is above the maximum supported version "
|
||||
"${NDK_MAX_PLATFORM_LEVEL}. Choose a supported API level or set "
|
||||
"ANDROID_PLATFORM to \"latest\".")
|
||||
endif()
|
||||
|
||||
set(${result_name} ${result} PARENT_SCOPE)
|
||||
endfunction()
|
||||
@ -0,0 +1,752 @@
|
||||
# Copyright (C) 2016 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Configurable variables.
|
||||
# Modeled after the ndk-build system.
|
||||
# For any variables defined in:
|
||||
# https://developer.android.com/ndk/guides/android_mk.html
|
||||
# https://developer.android.com/ndk/guides/application_mk.html
|
||||
# if it makes sense for CMake, then replace LOCAL, APP, or NDK with ANDROID, and
|
||||
# we have that variable below.
|
||||
#
|
||||
# ANDROID_TOOLCHAIN
|
||||
# ANDROID_ABI
|
||||
# ANDROID_PLATFORM
|
||||
# ANDROID_STL
|
||||
# ANDROID_PIE
|
||||
# ANDROID_CPP_FEATURES
|
||||
# ANDROID_ALLOW_UNDEFINED_SYMBOLS
|
||||
# ANDROID_ARM_MODE
|
||||
# ANDROID_DISABLE_FORMAT_STRING_CHECKS
|
||||
# ANDROID_CCACHE
|
||||
# ANDROID_SANITIZE
|
||||
|
||||
cmake_minimum_required(VERSION 3.12.0)
|
||||
|
||||
# Inhibit all of CMake's own NDK handling code.
|
||||
set(CMAKE_SYSTEM_VERSION 1)
|
||||
|
||||
# Android NDK
|
||||
get_filename_component(ANDROID_NDK_EXPECTED_PATH
|
||||
"${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE)
|
||||
if(NOT ANDROID_NDK)
|
||||
set(ANDROID_NDK "${ANDROID_NDK_EXPECTED_PATH}")
|
||||
else()
|
||||
# Allow the user to specify their own NDK path, but emit a warning. This is an
|
||||
# uncommon use case, but helpful if users want to use a bleeding edge
|
||||
# toolchain file with a stable NDK.
|
||||
# https://github.com/android-ndk/ndk/issues/473
|
||||
get_filename_component(ANDROID_NDK "${ANDROID_NDK}" ABSOLUTE)
|
||||
if(NOT "${ANDROID_NDK}" STREQUAL "${ANDROID_NDK_EXPECTED_PATH}")
|
||||
message(WARNING "Using custom NDK path (ANDROID_NDK is set): ${ANDROID_NDK}")
|
||||
endif()
|
||||
endif()
|
||||
unset(ANDROID_NDK_EXPECTED_PATH)
|
||||
file(TO_CMAKE_PATH "${ANDROID_NDK}" ANDROID_NDK)
|
||||
|
||||
# Android NDK revision
|
||||
# Possible formats:
|
||||
# * r16, build 1234: 16.0.1234
|
||||
# * r16b, build 1234: 16.1.1234
|
||||
# * r16 beta 1, build 1234: 16.0.1234-beta1
|
||||
#
|
||||
# Canary builds are not specially marked.
|
||||
file(READ "${ANDROID_NDK}/source.properties" ANDROID_NDK_SOURCE_PROPERTIES)
|
||||
|
||||
set(ANDROID_NDK_REVISION_REGEX
|
||||
"^Pkg\\.Desc = Android NDK\nPkg\\.Revision = ([0-9]+)\\.([0-9]+)\\.([0-9]+)(-beta([0-9]+))?")
|
||||
if(NOT ANDROID_NDK_SOURCE_PROPERTIES MATCHES "${ANDROID_NDK_REVISION_REGEX}")
|
||||
message(SEND_ERROR "Failed to parse Android NDK revision: ${ANDROID_NDK}/source.properties.\n${ANDROID_NDK_SOURCE_PROPERTIES}")
|
||||
endif()
|
||||
|
||||
set(ANDROID_NDK_MAJOR "${CMAKE_MATCH_1}")
|
||||
set(ANDROID_NDK_MINOR "${CMAKE_MATCH_2}")
|
||||
set(ANDROID_NDK_BUILD "${CMAKE_MATCH_3}")
|
||||
set(ANDROID_NDK_BETA "${CMAKE_MATCH_5}")
|
||||
if(ANDROID_NDK_BETA STREQUAL "")
|
||||
set(ANDROID_NDK_BETA "0")
|
||||
endif()
|
||||
set(ANDROID_NDK_REVISION
|
||||
"${ANDROID_NDK_MAJOR}.${ANDROID_NDK_MINOR}.${ANDROID_NDK_BUILD}${CMAKE_MATCH_4}")
|
||||
|
||||
# Touch toolchain variable to suppress "unused variable" warning.
|
||||
# This happens if CMake is invoked with the same command line the second time.
|
||||
if(CMAKE_TOOLCHAIN_FILE)
|
||||
endif()
|
||||
|
||||
# Compatibility for configurable variables.
|
||||
# Compatible with configurable variables from the other toolchain file:
|
||||
# https://github.com/taka-no-me/android-cmake
|
||||
# TODO: We should consider dropping compatibility to simplify things once most
|
||||
# of our users have migrated to our standard set of configurable variables.
|
||||
if(ANDROID_TOOLCHAIN_NAME AND NOT ANDROID_TOOLCHAIN)
|
||||
if(ANDROID_TOOLCHAIN_NAME MATCHES "-clang([0-9].[0-9])?$")
|
||||
set(ANDROID_TOOLCHAIN clang)
|
||||
elseif(ANDROID_TOOLCHAIN_NAME MATCHES "-[0-9].[0-9]$")
|
||||
set(ANDROID_TOOLCHAIN gcc)
|
||||
endif()
|
||||
endif()
|
||||
if(ANDROID_ABI STREQUAL "armeabi-v7a with NEON")
|
||||
set(ANDROID_ABI armeabi-v7a)
|
||||
elseif(ANDROID_TOOLCHAIN_NAME AND NOT ANDROID_ABI)
|
||||
if(ANDROID_TOOLCHAIN_NAME MATCHES "^arm-linux-androideabi-")
|
||||
set(ANDROID_ABI armeabi-v7a)
|
||||
elseif(ANDROID_TOOLCHAIN_NAME MATCHES "^aarch64-linux-android-")
|
||||
set(ANDROID_ABI arm64-v8a)
|
||||
elseif(ANDROID_TOOLCHAIN_NAME MATCHES "^x86-")
|
||||
set(ANDROID_ABI x86)
|
||||
elseif(ANDROID_TOOLCHAIN_NAME MATCHES "^x86_64-")
|
||||
set(ANDROID_ABI x86_64)
|
||||
elseif(ANDROID_TOOLCHAIN_NAME MATCHES "^mipsel-linux-android-")
|
||||
set(ANDROID_ABI mips)
|
||||
elseif(ANDROID_TOOLCHAIN_NAME MATCHES "^mips64el-linux-android-")
|
||||
set(ANDROID_ABI mips64)
|
||||
elseif(ANDROID_TOOLCHAIN_NAME MATCHES "^riscv64-")
|
||||
set(ANDROID_ABI riscv64)
|
||||
endif()
|
||||
endif()
|
||||
if(ANDROID_NATIVE_API_LEVEL AND NOT ANDROID_PLATFORM)
|
||||
if(ANDROID_NATIVE_API_LEVEL MATCHES "^android-[0-9]+$")
|
||||
set(ANDROID_PLATFORM ${ANDROID_NATIVE_API_LEVEL})
|
||||
elseif(ANDROID_NATIVE_API_LEVEL MATCHES "^[0-9]+$")
|
||||
set(ANDROID_PLATFORM android-${ANDROID_NATIVE_API_LEVEL})
|
||||
endif()
|
||||
endif()
|
||||
if(DEFINED ANDROID_APP_PIE AND NOT DEFINED ANDROID_PIE)
|
||||
set(ANDROID_PIE "${ANDROID_APP_PIE}")
|
||||
endif()
|
||||
if(ANDROID_STL_FORCE_FEATURES AND NOT DEFINED ANDROID_CPP_FEATURES)
|
||||
set(ANDROID_CPP_FEATURES "rtti exceptions")
|
||||
endif()
|
||||
if(DEFINED ANDROID_NO_UNDEFINED AND NOT DEFINED ANDROID_ALLOW_UNDEFINED_SYMBOLS)
|
||||
if(ANDROID_NO_UNDEFINED)
|
||||
set(ANDROID_ALLOW_UNDEFINED_SYMBOLS FALSE)
|
||||
else()
|
||||
set(ANDROID_ALLOW_UNDEFINED_SYMBOLS TRUE)
|
||||
endif()
|
||||
endif()
|
||||
if(DEFINED ANDROID_SO_UNDEFINED AND NOT DEFINED ANDROID_ALLOW_UNDEFINED_SYMBOLS)
|
||||
set(ANDROID_ALLOW_UNDEFINED_SYMBOLS "${ANDROID_SO_UNDEFINED}")
|
||||
endif()
|
||||
if(DEFINED ANDROID_FORCE_ARM_BUILD AND NOT ANDROID_ARM_MODE)
|
||||
if(ANDROID_FORCE_ARM_BUILD)
|
||||
set(ANDROID_ARM_MODE arm)
|
||||
else()
|
||||
set(ANDROID_ARM_MODE thumb)
|
||||
endif()
|
||||
endif()
|
||||
if(NDK_CCACHE AND NOT ANDROID_CCACHE)
|
||||
set(ANDROID_CCACHE "${NDK_CCACHE}")
|
||||
endif()
|
||||
|
||||
# Default values for configurable variables.
|
||||
if(NOT ANDROID_TOOLCHAIN)
|
||||
set(ANDROID_TOOLCHAIN clang)
|
||||
elseif(ANDROID_TOOLCHAIN STREQUAL gcc)
|
||||
message(FATAL_ERROR "GCC is no longer supported. See "
|
||||
"https://android.googlesource.com/platform/ndk/+/master/docs/ClangMigration.md.")
|
||||
endif()
|
||||
if(NOT ANDROID_ABI)
|
||||
set(ANDROID_ABI armeabi-v7a)
|
||||
endif()
|
||||
|
||||
if(ANDROID_ABI STREQUAL armeabi)
|
||||
message(FATAL_ERROR "armeabi is no longer supported. Use armeabi-v7a.")
|
||||
elseif(ANDROID_ABI MATCHES "^(mips|mips64)$")
|
||||
message(FATAL_ERROR "MIPS and MIPS64 are no longer supported.")
|
||||
endif()
|
||||
|
||||
if(DEFINED ANDROID_ARM_NEON AND NOT ANDROID_ARM_NEON)
|
||||
message(FATAL_ERROR "Disabling Neon is no longer supported")
|
||||
endif()
|
||||
|
||||
if(ANDROID_ABI STREQUAL armeabi-v7a)
|
||||
set(ANDROID_ARM_NEON TRUE)
|
||||
endif()
|
||||
|
||||
include(${ANDROID_NDK}/build/cmake/abis.cmake)
|
||||
include(${ANDROID_NDK}/build/cmake/platforms.cmake)
|
||||
|
||||
# If no platform version was chosen by the user, default to the minimum version
|
||||
# supported by this NDK.
|
||||
if(NOT ANDROID_PLATFORM)
|
||||
message(STATUS "\
|
||||
ANDROID_PLATFORM not set. Defaulting to minimum supported version
|
||||
${NDK_MIN_PLATFORM_LEVEL}.")
|
||||
|
||||
set(ANDROID_PLATFORM "android-${NDK_MIN_PLATFORM_LEVEL}")
|
||||
endif()
|
||||
|
||||
if(ANDROID_PLATFORM STREQUAL "latest")
|
||||
message(STATUS
|
||||
"Using latest available ANDROID_PLATFORM: ${NDK_MAX_PLATFORM_LEVEL}.")
|
||||
set(ANDROID_PLATFORM "android-${NDK_MAX_PLATFORM_LEVEL}")
|
||||
string(REPLACE "android-" "" ANDROID_PLATFORM_LEVEL ${ANDROID_PLATFORM})
|
||||
endif()
|
||||
|
||||
string(REPLACE "android-" "" ANDROID_PLATFORM_LEVEL ${ANDROID_PLATFORM})
|
||||
|
||||
# Aliases defined by meta/platforms.json include codename aliases for platform
|
||||
# API levels as well as cover any gaps in platforms that may not have had NDK
|
||||
# APIs.
|
||||
if(NOT "${NDK_PLATFORM_ALIAS_${ANDROID_PLATFORM_LEVEL}}" STREQUAL "")
|
||||
message(STATUS "\
|
||||
${ANDROID_PLATFORM} is an alias for \
|
||||
${NDK_PLATFORM_ALIAS_${ANDROID_PLATFORM_LEVEL}}. Adjusting ANDROID_PLATFORM to \
|
||||
match.")
|
||||
set(ANDROID_PLATFORM "${NDK_PLATFORM_ALIAS_${ANDROID_PLATFORM_LEVEL}}")
|
||||
string(REPLACE "android-" "" ANDROID_PLATFORM_LEVEL ${ANDROID_PLATFORM})
|
||||
endif()
|
||||
|
||||
# Pull up to the minimum supported version if an old API level was requested.
|
||||
if(ANDROID_PLATFORM_LEVEL LESS NDK_MIN_PLATFORM_LEVEL)
|
||||
message(STATUS "\
|
||||
${ANDROID_PLATFORM} is unsupported. Using minimum supported version \
|
||||
${NDK_MIN_PLATFORM_LEVEL}.")
|
||||
set(ANDROID_PLATFORM "android-${NDK_MIN_PLATFORM_LEVEL}")
|
||||
string(REPLACE "android-" "" ANDROID_PLATFORM_LEVEL ${ANDROID_PLATFORM})
|
||||
endif()
|
||||
|
||||
# Pull up any ABI-specific minimum API levels.
|
||||
set(min_for_abi ${NDK_ABI_${ANDROID_ABI}_MIN_OS_VERSION})
|
||||
|
||||
if(ANDROID_PLATFORM_LEVEL LESS min_for_abi)
|
||||
message(STATUS
|
||||
"${ANDROID_PLATFORM} is not supported for ${ANDROID_ABI}. Using minimum "
|
||||
"supported ${ANDROID_ABI} version ${min_for_abi}.")
|
||||
set(ANDROID_PLATFORM android-${min_for_abi})
|
||||
set(ANDROID_PLATFORM_LEVEL ${min_for_abi})
|
||||
endif()
|
||||
|
||||
# ANDROID_PLATFORM beyond the maximum is an error. The correct way to specify
|
||||
# the latest version is ANDROID_PLATFORM=latest.
|
||||
if(ANDROID_PLATFORM_LEVEL GREATER NDK_MAX_PLATFORM_LEVEL)
|
||||
message(SEND_ERROR "\
|
||||
${ANDROID_PLATFORM} is above the maximum supported version \
|
||||
${NDK_MAX_PLATFORM_LEVEL}. Choose a supported API level or set \
|
||||
ANDROID_PLATFORM to \"latest\".")
|
||||
endif()
|
||||
|
||||
if(NOT ANDROID_STL)
|
||||
set(ANDROID_STL c++_static)
|
||||
endif()
|
||||
|
||||
if("${ANDROID_STL}" STREQUAL "gnustl_shared" OR
|
||||
"${ANDROID_STL}" STREQUAL "gnustl_static" OR
|
||||
"${ANDROID_STL}" STREQUAL "stlport_shared" OR
|
||||
"${ANDROID_STL}" STREQUAL "stlport_static")
|
||||
message(FATAL_ERROR "\
|
||||
${ANDROID_STL} is no longer supported. Please switch to either c++_shared or \
|
||||
c++_static. See https://developer.android.com/ndk/guides/cpp-support.html \
|
||||
for more information.")
|
||||
endif()
|
||||
|
||||
if("hwaddress" IN_LIST ANDROID_SANITIZE AND "${CMAKE_ANDROID_STL_TYPE}" STREQUAL "c++_static")
|
||||
message(FATAL_ERROR "\
|
||||
hwaddress does not support c++_static. Use system or c++_shared.")
|
||||
endif()
|
||||
|
||||
set(ANDROID_PIE TRUE)
|
||||
if(NOT ANDROID_ARM_MODE)
|
||||
set(ANDROID_ARM_MODE thumb)
|
||||
endif()
|
||||
|
||||
# Export configurable variables for the try_compile() command.
|
||||
set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES
|
||||
ANDROID_ABI
|
||||
ANDROID_ALLOW_UNDEFINED_SYMBOLS
|
||||
ANDROID_ARM_MODE
|
||||
ANDROID_ARM_NEON
|
||||
ANDROID_CCACHE
|
||||
ANDROID_CPP_FEATURES
|
||||
ANDROID_DISABLE_FORMAT_STRING_CHECKS
|
||||
ANDROID_PIE
|
||||
ANDROID_PLATFORM
|
||||
ANDROID_STL
|
||||
ANDROID_TOOLCHAIN
|
||||
ANDROID_USE_LEGACY_TOOLCHAIN_FILE
|
||||
)
|
||||
|
||||
# Standard cross-compiling stuff.
|
||||
set(ANDROID TRUE)
|
||||
set(CMAKE_SYSTEM_NAME Android)
|
||||
|
||||
# https://github.com/android-ndk/ndk/issues/890
|
||||
#
|
||||
# ONLY doesn't do anything when CMAKE_FIND_ROOT_PATH is empty. Without this,
|
||||
# CMake will wrongly search host sysroots for headers/libraries. The actual path
|
||||
# used here is fairly meaningless since CMake doesn't handle the NDK sysroot
|
||||
# layout (per-arch and per-verion subdirectories for libraries), so find_library
|
||||
# is handled separately by CMAKE_SYSTEM_LIBRARY_PATH.
|
||||
list(APPEND CMAKE_FIND_ROOT_PATH "${ANDROID_NDK}")
|
||||
|
||||
# Allow users to override these values in case they want more strict behaviors.
|
||||
# For example, they may want to prevent the NDK's libz from being picked up so
|
||||
# they can use their own.
|
||||
# https://github.com/android-ndk/ndk/issues/517
|
||||
if(NOT CMAKE_FIND_ROOT_PATH_MODE_PROGRAM)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_FIND_ROOT_PATH_MODE_LIBRARY)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_FIND_ROOT_PATH_MODE_INCLUDE)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_FIND_ROOT_PATH_MODE_PACKAGE)
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
||||
endif()
|
||||
|
||||
# ABI.
|
||||
set(CMAKE_ANDROID_ARCH_ABI ${ANDROID_ABI})
|
||||
if(ANDROID_ABI STREQUAL armeabi-v7a)
|
||||
set(ANDROID_SYSROOT_ABI arm)
|
||||
set(ANDROID_TOOLCHAIN_NAME arm-linux-androideabi)
|
||||
set(CMAKE_SYSTEM_PROCESSOR armv7-a)
|
||||
set(ANDROID_LLVM_TRIPLE armv7-none-linux-androideabi)
|
||||
elseif(ANDROID_ABI STREQUAL arm64-v8a)
|
||||
set(ANDROID_SYSROOT_ABI arm64)
|
||||
set(CMAKE_SYSTEM_PROCESSOR aarch64)
|
||||
set(ANDROID_TOOLCHAIN_NAME aarch64-linux-android)
|
||||
set(ANDROID_LLVM_TRIPLE aarch64-none-linux-android)
|
||||
elseif(ANDROID_ABI STREQUAL x86)
|
||||
set(ANDROID_SYSROOT_ABI x86)
|
||||
set(CMAKE_SYSTEM_PROCESSOR i686)
|
||||
set(ANDROID_TOOLCHAIN_NAME i686-linux-android)
|
||||
set(ANDROID_LLVM_TRIPLE i686-none-linux-android)
|
||||
elseif(ANDROID_ABI STREQUAL x86_64)
|
||||
set(ANDROID_SYSROOT_ABI x86_64)
|
||||
set(CMAKE_SYSTEM_PROCESSOR x86_64)
|
||||
set(ANDROID_TOOLCHAIN_NAME x86_64-linux-android)
|
||||
set(ANDROID_LLVM_TRIPLE x86_64-none-linux-android)
|
||||
elseif(ANDROID_ABI STREQUAL riscv64)
|
||||
set(ANDROID_SYSROOT_ABI riscv64)
|
||||
set(CMAKE_SYSTEM_PROCESSOR riscv64)
|
||||
set(ANDROID_TOOLCHAIN_NAME riscv64-linux-android)
|
||||
set(ANDROID_LLVM_TRIPLE riscv64-none-linux-android)
|
||||
else()
|
||||
message(FATAL_ERROR "Invalid Android ABI: ${ANDROID_ABI}.")
|
||||
endif()
|
||||
|
||||
set(ANDROID_LLVM_TRIPLE "${ANDROID_LLVM_TRIPLE}${ANDROID_PLATFORM_LEVEL}")
|
||||
|
||||
set(ANDROID_COMPILER_FLAGS)
|
||||
set(ANDROID_COMPILER_FLAGS_CXX)
|
||||
set(ANDROID_COMPILER_FLAGS_DEBUG)
|
||||
set(ANDROID_COMPILER_FLAGS_RELEASE)
|
||||
set(ANDROID_LINKER_FLAGS)
|
||||
set(ANDROID_LINKER_FLAGS_EXE)
|
||||
set(ANDROID_LINKER_FLAGS_RELEASE)
|
||||
set(ANDROID_LINKER_FLAGS_RELWITHDEBINFO)
|
||||
set(ANDROID_LINKER_FLAGS_MINSIZEREL)
|
||||
|
||||
# STL.
|
||||
set(ANDROID_CXX_STANDARD_LIBRARIES)
|
||||
if(ANDROID_STL STREQUAL system)
|
||||
list(APPEND ANDROID_COMPILER_FLAGS_CXX "-stdlib=libstdc++")
|
||||
if(NOT "x${ANDROID_CPP_FEATURES}" STREQUAL "x")
|
||||
list(APPEND ANDROID_CXX_STANDARD_LIBRARIES "-lc++abi")
|
||||
endif()
|
||||
elseif(ANDROID_STL STREQUAL c++_static)
|
||||
list(APPEND ANDROID_LINKER_FLAGS "-static-libstdc++")
|
||||
elseif(ANDROID_STL STREQUAL c++_shared)
|
||||
elseif(ANDROID_STL STREQUAL none)
|
||||
list(APPEND ANDROID_COMPILER_FLAGS_CXX "-nostdinc++")
|
||||
list(APPEND ANDROID_LINKER_FLAGS "-nostdlib++")
|
||||
else()
|
||||
message(FATAL_ERROR "Invalid STL: ${ANDROID_STL}.")
|
||||
endif()
|
||||
|
||||
if(CMAKE_HOST_SYSTEM_NAME STREQUAL Linux)
|
||||
set(ANDROID_HOST_TAG linux-x86_64)
|
||||
elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL Darwin)
|
||||
set(ANDROID_HOST_TAG darwin-x86_64)
|
||||
elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL Windows)
|
||||
set(ANDROID_HOST_TAG windows-x86_64)
|
||||
endif()
|
||||
|
||||
if(CMAKE_HOST_SYSTEM_NAME STREQUAL Windows)
|
||||
set(ANDROID_TOOLCHAIN_SUFFIX .exe)
|
||||
endif()
|
||||
|
||||
# Toolchain.
|
||||
set(ANDROID_TOOLCHAIN_ROOT
|
||||
"${ANDROID_NDK}/toolchains/llvm/prebuilt/${ANDROID_HOST_TAG}")
|
||||
|
||||
list(APPEND CMAKE_PREFIX_PATH "${ANDROID_TOOLCHAIN_ROOT}")
|
||||
|
||||
# NB: This variable causes CMake to automatically pass --sysroot to the
|
||||
# toolchain. Studio currently relies on this to recognize Android builds. If
|
||||
# this variable is removed, ensure that flag is still passed.
|
||||
# TODO: Teach Studio to recognize Android builds based on --target.
|
||||
set(CMAKE_SYSROOT "${ANDROID_TOOLCHAIN_ROOT}/sysroot")
|
||||
|
||||
# Allows CMake to find headers in the architecture-specific include directories.
|
||||
set(CMAKE_LIBRARY_ARCHITECTURE "${ANDROID_TOOLCHAIN_NAME}")
|
||||
|
||||
# In addition to <root>/<prefix>/lib/<arch>, cmake also searches <root>/<prefix>.
|
||||
# Adding the API specific path to the beginning of CMAKE_SYSTEM_PREFIX_PATH, to
|
||||
# make sure it is searched first.
|
||||
set(CMAKE_SYSTEM_PREFIX_PATH
|
||||
"/usr/lib/${ANDROID_TOOLCHAIN_NAME}/${ANDROID_PLATFORM_LEVEL}"
|
||||
"${CMAKE_SYSTEM_PREFIX_PATH}"
|
||||
)
|
||||
|
||||
set(ANDROID_HOST_PREBUILTS "${ANDROID_NDK}/prebuilt/${ANDROID_HOST_TAG}")
|
||||
|
||||
set(ANDROID_C_COMPILER
|
||||
"${ANDROID_TOOLCHAIN_ROOT}/bin/clang${ANDROID_TOOLCHAIN_SUFFIX}")
|
||||
set(ANDROID_CXX_COMPILER
|
||||
"${ANDROID_TOOLCHAIN_ROOT}/bin/clang++${ANDROID_TOOLCHAIN_SUFFIX}")
|
||||
set(ANDROID_ASM_COMPILER
|
||||
"${ANDROID_TOOLCHAIN_ROOT}/bin/clang${ANDROID_TOOLCHAIN_SUFFIX}")
|
||||
set(CMAKE_C_COMPILER_TARGET ${ANDROID_LLVM_TRIPLE})
|
||||
set(CMAKE_CXX_COMPILER_TARGET ${ANDROID_LLVM_TRIPLE})
|
||||
set(CMAKE_ASM_COMPILER_TARGET ${ANDROID_LLVM_TRIPLE})
|
||||
set(ANDROID_AR
|
||||
"${ANDROID_TOOLCHAIN_ROOT}/bin/llvm-ar${ANDROID_TOOLCHAIN_SUFFIX}")
|
||||
set(ANDROID_RANLIB
|
||||
"${ANDROID_TOOLCHAIN_ROOT}/bin/llvm-ranlib${ANDROID_TOOLCHAIN_SUFFIX}")
|
||||
set(ANDROID_STRIP
|
||||
"${ANDROID_TOOLCHAIN_ROOT}/bin/llvm-strip${ANDROID_TOOLCHAIN_SUFFIX}")
|
||||
|
||||
if(${CMAKE_VERSION} VERSION_LESS "3.19")
|
||||
# Older CMake won't pass -target when running the compiler identification
|
||||
# test, which causes the test to fail on flags like -mthumb.
|
||||
# https://github.com/android/ndk/issues/1427
|
||||
message(WARNING "An old version of CMake is being used that cannot "
|
||||
"automatically detect compiler attributes. Compiler identification is "
|
||||
"being bypassed. Some values may be wrong or missing. Update to CMake "
|
||||
"3.19 or newer to use CMake's built-in compiler identification.")
|
||||
set(CMAKE_C_COMPILER_ID_RUN TRUE)
|
||||
set(CMAKE_CXX_COMPILER_ID_RUN TRUE)
|
||||
set(CMAKE_C_COMPILER_ID Clang)
|
||||
set(CMAKE_CXX_COMPILER_ID Clang)
|
||||
# No need to auto-detect the computed standard defaults because CMake 3.6
|
||||
# doesn't know about anything past C11 or C++14 (neither does 3.10, so no
|
||||
# need to worry about 3.7-3.9), and any higher standards that Clang might
|
||||
# use are clamped to those values.
|
||||
set(CMAKE_C_STANDARD_COMPUTED_DEFAULT 11)
|
||||
set(CMAKE_CXX_STANDARD_COMPUTED_DEFAULT 14)
|
||||
set(CMAKE_C_COMPILER_FRONTEND_VARIANT "GNU")
|
||||
set(CMAKE_CXX_COMPILER_FRONTEND_VARIANT "GNU")
|
||||
include(${ANDROID_NDK}/build/cmake/compiler_id.cmake)
|
||||
endif()
|
||||
|
||||
# Generic flags.
|
||||
list(APPEND ANDROID_COMPILER_FLAGS
|
||||
-g
|
||||
-DANDROID
|
||||
-fdata-sections
|
||||
-ffunction-sections
|
||||
-funwind-tables
|
||||
-fstack-protector-strong
|
||||
-no-canonical-prefixes)
|
||||
|
||||
if(ANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES)
|
||||
list(APPEND ANDROID_COMPILER_FLAGS -D__BIONIC_NO_PAGE_SIZE_MACRO)
|
||||
if(ANDROID_ABI STREQUAL arm64-v8a OR ANDROID_ABI STREQUAL x86_64)
|
||||
list(APPEND ANDROID_LINKER_FLAGS -Wl,-z,max-page-size=16384)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ANDROID_WEAK_API_DEFS)
|
||||
list(APPEND ANDROID_COMPILER_FLAGS
|
||||
-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
|
||||
-Werror=unguarded-availability)
|
||||
endif()
|
||||
|
||||
if("hwaddress" IN_LIST ANDROID_SANITIZE)
|
||||
list(APPEND ANDROID_COMPILER_FLAGS -fsanitize=hwaddress -fno-omit-frame-pointer)
|
||||
list(APPEND ANDROID_LINKER_FLAGS -fsanitize=hwaddress)
|
||||
endif()
|
||||
|
||||
if("memtag" IN_LIST ANDROID_SANITIZE)
|
||||
list(APPEND ANDROID_COMPILER_FLAGS -fsanitize=memtag-stack -fno-omit-frame-pointer)
|
||||
list(APPEND ANDROID_LINKER_FLAGS -fsanitize=memtag-stack,memtag-heap -fsanitize-memtag-mode=sync)
|
||||
if(ANDROID_ABI STREQUAL arm64-v8a)
|
||||
list(APPEND ANDROID_COMPILER_FLAGS -march=armv8-a+memtag)
|
||||
list(APPEND ANDROID_LINKER_FLAGS -march=armv8-a+memtag)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# https://github.com/android/ndk/issues/885
|
||||
# If we're using LLD we need to use a slower build-id algorithm to work around
|
||||
# the old version of LLDB in Android Studio, which doesn't understand LLD's
|
||||
# default hash ("fast").
|
||||
list(APPEND ANDROID_LINKER_FLAGS -Wl,--build-id=sha1)
|
||||
if(ANDROID_PLATFORM_LEVEL LESS 30)
|
||||
# https://github.com/android/ndk/issues/1196
|
||||
# https://github.com/android/ndk/issues/1589
|
||||
list(APPEND ANDROID_LINKER_FLAGS -Wl,--no-rosegment)
|
||||
endif()
|
||||
|
||||
if (NOT ANDROID_ALLOW_UNDEFINED_VERSION_SCRIPT_SYMBOLS)
|
||||
list(APPEND ANDROID_LINKER_FLAGS -Wl,--no-undefined-version)
|
||||
endif()
|
||||
|
||||
list(APPEND ANDROID_LINKER_FLAGS -Wl,--fatal-warnings)
|
||||
|
||||
# --gc-sections should not be present for debug builds because that can strip
|
||||
# functions that the user may want to evaluate while debugging.
|
||||
list(APPEND ANDROID_LINKER_FLAGS_RELEASE -Wl,--gc-sections)
|
||||
list(APPEND ANDROID_LINKER_FLAGS_RELWITHDEBINFO -Wl,--gc-sections)
|
||||
list(APPEND ANDROID_LINKER_FLAGS_MINSIZEREL -Wl,--gc-sections)
|
||||
|
||||
# Debug and release flags.
|
||||
list(APPEND ANDROID_COMPILER_FLAGS_RELEASE -O3)
|
||||
list(APPEND ANDROID_COMPILER_FLAGS_RELEASE -DNDEBUG)
|
||||
if(ANDROID_TOOLCHAIN STREQUAL clang)
|
||||
list(APPEND ANDROID_COMPILER_FLAGS_DEBUG -fno-limit-debug-info)
|
||||
endif()
|
||||
|
||||
# Toolchain and ABI specific flags.
|
||||
if(ANDROID_ABI STREQUAL x86 AND ANDROID_PLATFORM_LEVEL LESS 24)
|
||||
# http://b.android.com/222239
|
||||
# http://b.android.com/220159 (internal http://b/31809417)
|
||||
# x86 devices have stack alignment issues.
|
||||
list(APPEND ANDROID_COMPILER_FLAGS -mstackrealign)
|
||||
endif()
|
||||
|
||||
list(APPEND ANDROID_COMPILER_FLAGS -D_FORTIFY_SOURCE=2)
|
||||
|
||||
set(CMAKE_C_STANDARD_LIBRARIES_INIT "-latomic -lm")
|
||||
set(CMAKE_CXX_STANDARD_LIBRARIES_INIT "${CMAKE_C_STANDARD_LIBRARIES_INIT}")
|
||||
if(ANDROID_CXX_STANDARD_LIBRARIES)
|
||||
string(REPLACE ";" "\" \"" ANDROID_CXX_STANDARD_LIBRARIES "\"${ANDROID_CXX_STANDARD_LIBRARIES}\"")
|
||||
set(CMAKE_CXX_STANDARD_LIBRARIES_INIT "${CMAKE_CXX_STANDARD_LIBRARIES_INIT} ${ANDROID_CXX_STANDARD_LIBRARIES}")
|
||||
endif()
|
||||
|
||||
# Configuration specific flags.
|
||||
|
||||
# PIE is supported on all currently supported Android releases, but it is not
|
||||
# supported with static executables, so we still provide ANDROID_PIE as an
|
||||
# escape hatch for those.
|
||||
if(ANDROID_PIE)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
|
||||
endif()
|
||||
|
||||
if(ANDROID_CPP_FEATURES)
|
||||
separate_arguments(ANDROID_CPP_FEATURES)
|
||||
foreach(feature ${ANDROID_CPP_FEATURES})
|
||||
if(NOT ${feature} MATCHES "^(rtti|exceptions|no-rtti|no-exceptions)$")
|
||||
message(FATAL_ERROR "Invalid Android C++ feature: ${feature}.")
|
||||
endif()
|
||||
list(APPEND ANDROID_COMPILER_FLAGS_CXX
|
||||
-f${feature})
|
||||
endforeach()
|
||||
string(REPLACE ";" " " ANDROID_CPP_FEATURES "${ANDROID_CPP_FEATURES}")
|
||||
endif()
|
||||
if(NOT ANDROID_ALLOW_UNDEFINED_SYMBOLS)
|
||||
list(APPEND ANDROID_LINKER_FLAGS
|
||||
-Wl,--no-undefined)
|
||||
endif()
|
||||
if(ANDROID_ABI MATCHES "armeabi")
|
||||
# Clang does not set this up properly when using -fno-integrated-as.
|
||||
# https://github.com/android-ndk/ndk/issues/906
|
||||
list(APPEND ANDROID_COMPILER_FLAGS "-march=armv7-a")
|
||||
if(ANDROID_ARM_MODE STREQUAL thumb)
|
||||
list(APPEND ANDROID_COMPILER_FLAGS -mthumb)
|
||||
elseif(ANDROID_ARM_MODE STREQUAL arm)
|
||||
# Default behavior.
|
||||
else()
|
||||
message(FATAL_ERROR "Invalid Android ARM mode: ${ANDROID_ARM_MODE}.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# CMake automatically forwards all compiler flags to the linker, and clang
|
||||
# doesn't like having -Wa flags being used for linking. To prevent CMake from
|
||||
# doing this would require meddling with the CMAKE_<LANG>_COMPILE_OBJECT rules,
|
||||
# which would get quite messy.
|
||||
list(APPEND ANDROID_LINKER_FLAGS -Qunused-arguments)
|
||||
|
||||
if(ANDROID_DISABLE_FORMAT_STRING_CHECKS)
|
||||
list(APPEND ANDROID_COMPILER_FLAGS
|
||||
-Wno-error=format-security)
|
||||
else()
|
||||
list(APPEND ANDROID_COMPILER_FLAGS
|
||||
-Wformat -Werror=format-security)
|
||||
endif()
|
||||
|
||||
# Convert these lists into strings.
|
||||
string(REPLACE ";" " " ANDROID_COMPILER_FLAGS "${ANDROID_COMPILER_FLAGS}")
|
||||
string(REPLACE ";" " " ANDROID_COMPILER_FLAGS_CXX "${ANDROID_COMPILER_FLAGS_CXX}")
|
||||
string(REPLACE ";" " " ANDROID_COMPILER_FLAGS_DEBUG "${ANDROID_COMPILER_FLAGS_DEBUG}")
|
||||
string(REPLACE ";" " " ANDROID_COMPILER_FLAGS_RELEASE "${ANDROID_COMPILER_FLAGS_RELEASE}")
|
||||
string(REPLACE ";" " " ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS}")
|
||||
string(REPLACE ";" " " ANDROID_LINKER_FLAGS_EXE "${ANDROID_LINKER_FLAGS_EXE}")
|
||||
string(REPLACE ";" " " ANDROID_LINKER_FLAGS_RELEASE "${ANDROID_LINKER_FLAGS_RELEASE}")
|
||||
string(REPLACE ";" " " ANDROID_LINKER_FLAGS_RELWITHDEBINFO "${ANDROID_LINKER_FLAGS_RELWITHDEBINFO}")
|
||||
string(REPLACE ";" " " ANDROID_LINKER_FLAGS_MINSIZEREL "${ANDROID_LINKER_FLAGS_MINSIZEREL}")
|
||||
|
||||
if(ANDROID_CCACHE)
|
||||
set(CMAKE_C_COMPILER_LAUNCHER "${ANDROID_CCACHE}")
|
||||
set(CMAKE_CXX_COMPILER_LAUNCHER "${ANDROID_CCACHE}")
|
||||
endif()
|
||||
set(CMAKE_C_COMPILER "${ANDROID_C_COMPILER}")
|
||||
set(CMAKE_CXX_COMPILER "${ANDROID_CXX_COMPILER}")
|
||||
set(CMAKE_AR "${ANDROID_AR}" CACHE FILEPATH "Archiver")
|
||||
set(CMAKE_RANLIB "${ANDROID_RANLIB}" CACHE FILEPATH "Ranlib")
|
||||
set(CMAKE_STRIP "${ANDROID_STRIP}" CACHE FILEPATH "Strip")
|
||||
|
||||
if(ANDROID_ABI STREQUAL "x86" OR ANDROID_ABI STREQUAL "x86_64")
|
||||
set(CMAKE_ASM_NASM_COMPILER
|
||||
"${ANDROID_TOOLCHAIN_ROOT}/bin/yasm${ANDROID_TOOLCHAIN_SUFFIX}")
|
||||
set(CMAKE_ASM_NASM_COMPILER_ARG1 "-DELF")
|
||||
endif()
|
||||
|
||||
# Set or retrieve the cached flags.
|
||||
# This is necessary in case the user sets/changes flags in subsequent
|
||||
# configures. If we included the Android flags in here, they would get
|
||||
# overwritten.
|
||||
set(CMAKE_C_FLAGS ""
|
||||
CACHE STRING "Flags used by the compiler during all build types.")
|
||||
set(CMAKE_CXX_FLAGS ""
|
||||
CACHE STRING "Flags used by the compiler during all build types.")
|
||||
set(CMAKE_ASM_FLAGS ""
|
||||
CACHE STRING "Flags used by the compiler during all build types.")
|
||||
set(CMAKE_C_FLAGS_DEBUG ""
|
||||
CACHE STRING "Flags used by the compiler during debug builds.")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG ""
|
||||
CACHE STRING "Flags used by the compiler during debug builds.")
|
||||
set(CMAKE_ASM_FLAGS_DEBUG ""
|
||||
CACHE STRING "Flags used by the compiler during debug builds.")
|
||||
set(CMAKE_C_FLAGS_RELEASE ""
|
||||
CACHE STRING "Flags used by the compiler during release builds.")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE ""
|
||||
CACHE STRING "Flags used by the compiler during release builds.")
|
||||
set(CMAKE_ASM_FLAGS_RELEASE ""
|
||||
CACHE STRING "Flags used by the compiler during release builds.")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS ""
|
||||
CACHE STRING "Flags used by the linker during the creation of modules.")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS ""
|
||||
CACHE STRING "Flags used by the linker during the creation of dll's.")
|
||||
set(CMAKE_EXE_LINKER_FLAGS ""
|
||||
CACHE STRING "Flags used by the linker.")
|
||||
|
||||
set(CMAKE_C_FLAGS "${ANDROID_COMPILER_FLAGS} ${CMAKE_C_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${ANDROID_COMPILER_FLAGS} ${ANDROID_COMPILER_FLAGS_CXX} ${CMAKE_CXX_FLAGS}")
|
||||
set(CMAKE_ASM_FLAGS "${ANDROID_COMPILER_FLAGS} ${CMAKE_ASM_FLAGS}")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${ANDROID_COMPILER_FLAGS_DEBUG} ${CMAKE_C_FLAGS_DEBUG}")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${ANDROID_COMPILER_FLAGS_DEBUG} ${CMAKE_CXX_FLAGS_DEBUG}")
|
||||
set(CMAKE_ASM_FLAGS_DEBUG "${ANDROID_COMPILER_FLAGS_DEBUG} ${CMAKE_ASM_FLAGS_DEBUG}")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${ANDROID_COMPILER_FLAGS_RELEASE} ${CMAKE_C_FLAGS_RELEASE}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${ANDROID_COMPILER_FLAGS_RELEASE} ${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
set(CMAKE_ASM_FLAGS_RELEASE "${ANDROID_COMPILER_FLAGS_RELEASE} ${CMAKE_ASM_FLAGS_RELEASE}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${ANDROID_LINKER_FLAGS_EXE} ${CMAKE_EXE_LINKER_FLAGS}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${ANDROID_LINKER_FLAGS_RELEASE} ${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${ANDROID_LINKER_FLAGS_RELEASE} ${CMAKE_MODULE_LINKER_FLAGS_RELEASE}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${ANDROID_LINKER_FLAGS_RELEASE} ${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${ANDROID_LINKER_FLAGS_RELWITHDEBINFO} ${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO "${ANDROID_LINKER_FLAGS_RELWITHDEBINFO} ${CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${ANDROID_LINKER_FLAGS_RELWITHDEBINFO} ${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL "${ANDROID_LINKER_FLAGS_MINSIZEREL} ${CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL "${ANDROID_LINKER_FLAGS_MINSIZEREL} ${CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "${ANDROID_LINKER_FLAGS_MINSIZEREL} ${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL}")
|
||||
|
||||
# Compatibility for read-only variables.
|
||||
# Read-only variables for compatibility with the other toolchain file.
|
||||
# We'll keep these around for the existing projects that still use them.
|
||||
# TODO: All of the variables here have equivalents in our standard set of
|
||||
# configurable variables, so we can remove these once most of our users migrate
|
||||
# to those variables.
|
||||
set(ANDROID_NATIVE_API_LEVEL ${ANDROID_PLATFORM_LEVEL})
|
||||
if(ANDROID_ALLOW_UNDEFINED_SYMBOLS)
|
||||
set(ANDROID_SO_UNDEFINED TRUE)
|
||||
else()
|
||||
set(ANDROID_NO_UNDEFINED TRUE)
|
||||
endif()
|
||||
set(ANDROID_FUNCTION_LEVEL_LINKING TRUE)
|
||||
set(ANDROID_GOLD_LINKER TRUE)
|
||||
set(ANDROID_NOEXECSTACK TRUE)
|
||||
set(ANDROID_RELRO TRUE)
|
||||
if(ANDROID_ARM_MODE STREQUAL arm)
|
||||
set(ANDROID_FORCE_ARM_BUILD TRUE)
|
||||
endif()
|
||||
if(ANDROID_CPP_FEATURES MATCHES "rtti"
|
||||
AND ANDROID_CPP_FEATURES MATCHES "exceptions")
|
||||
set(ANDROID_STL_FORCE_FEATURES TRUE)
|
||||
endif()
|
||||
if(ANDROID_CCACHE)
|
||||
set(NDK_CCACHE "${ANDROID_CCACHE}")
|
||||
endif()
|
||||
if(ANDROID_TOOLCHAIN STREQUAL clang)
|
||||
set(ANDROID_TOOLCHAIN_NAME ${ANDROID_TOOLCHAIN_NAME}-clang)
|
||||
else()
|
||||
set(ANDROID_TOOLCHAIN_NAME ${ANDROID_TOOLCHAIN_NAME}-4.9)
|
||||
endif()
|
||||
set(ANDROID_NDK_HOST_X64 TRUE)
|
||||
set(ANDROID_NDK_LAYOUT RELEASE)
|
||||
if(ANDROID_ABI STREQUAL armeabi-v7a)
|
||||
set(ARMEABI_V7A TRUE)
|
||||
if(ANDROID_ARM_NEON)
|
||||
set(NEON TRUE)
|
||||
endif()
|
||||
elseif(ANDROID_ABI STREQUAL arm64-v8a)
|
||||
set(ARM64_V8A TRUE)
|
||||
elseif(ANDROID_ABI STREQUAL x86)
|
||||
set(X86 TRUE)
|
||||
elseif(ANDROID_ABI STREQUAL x86_64)
|
||||
set(X86_64 TRUE)
|
||||
elseif(ANDROID_ABI STREQUAL riscv64)
|
||||
set(RISCV64 TRUE)
|
||||
endif()
|
||||
set(ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_HOST_TAG})
|
||||
set(ANDROID_NDK_ABI_NAME ${ANDROID_ABI})
|
||||
set(ANDROID_NDK_RELEASE r${ANDROID_NDK_REVISION})
|
||||
set(ANDROID_ARCH_NAME ${ANDROID_SYSROOT_ABI})
|
||||
set(TOOL_OS_SUFFIX ${ANDROID_TOOLCHAIN_SUFFIX})
|
||||
if(ANDROID_TOOLCHAIN STREQUAL clang)
|
||||
set(ANDROID_COMPILER_IS_CLANG TRUE)
|
||||
endif()
|
||||
|
||||
# CMake 3.7+ compatibility.
|
||||
if (CMAKE_VERSION VERSION_GREATER 3.7.0)
|
||||
set(CMAKE_ANDROID_NDK ${ANDROID_NDK})
|
||||
set(CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION clang)
|
||||
|
||||
set(CMAKE_ANDROID_STL_TYPE ${ANDROID_STL})
|
||||
|
||||
if(ANDROID_ABI MATCHES "^armeabi(-v7a)?$")
|
||||
set(CMAKE_ANDROID_ARM_NEON ${ANDROID_ARM_NEON})
|
||||
set(CMAKE_ANDROID_ARM_MODE ${ANDROID_ARM_MODE})
|
||||
endif()
|
||||
|
||||
# https://github.com/android/ndk/issues/861
|
||||
if(ANDROID_ABI STREQUAL armeabi-v7a)
|
||||
set(CMAKE_ANDROID_ARCH arm)
|
||||
elseif(ANDROID_ABI STREQUAL arm64-v8a)
|
||||
set(CMAKE_ANDROID_ARCH arm64)
|
||||
elseif(ANDROID_ABI STREQUAL x86)
|
||||
set(CMAKE_ANDROID_ARCH x86)
|
||||
elseif(ANDROID_ABI STREQUAL x86_64)
|
||||
set(CMAKE_ANDROID_ARCH x86_64)
|
||||
elseif(ANDROID_ABI STREQUAL riscv64)
|
||||
set(CMAKE_ANDROID_ARCH riscv64)
|
||||
endif()
|
||||
|
||||
# https://github.com/android/ndk/issues/1012
|
||||
set(CMAKE_ASM_ANDROID_TOOLCHAIN_MACHINE "${ANDROID_TOOLCHAIN_NAME}")
|
||||
set(CMAKE_C_ANDROID_TOOLCHAIN_MACHINE "${ANDROID_TOOLCHAIN_NAME}")
|
||||
set(CMAKE_CXX_ANDROID_TOOLCHAIN_MACHINE "${ANDROID_TOOLCHAIN_NAME}")
|
||||
|
||||
set(CMAKE_ASM_ANDROID_TOOLCHAIN_SUFFIX "${ANDROID_TOOLCHAIN_SUFFIX}")
|
||||
set(CMAKE_C_ANDROID_TOOLCHAIN_SUFFIX "${ANDROID_TOOLCHAIN_SUFFIX}")
|
||||
set(CMAKE_CXX_ANDROID_TOOLCHAIN_SUFFIX "${ANDROID_TOOLCHAIN_SUFFIX}")
|
||||
endif()
|
||||
291
Android/android-ndk-r27d/build/cmake/android.toolchain.cmake
Normal file
@ -0,0 +1,291 @@
|
||||
# Copyright (C) 2016 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Configurable variables.
|
||||
# Modeled after the ndk-build system.
|
||||
# For any variables defined in:
|
||||
# https://developer.android.com/ndk/guides/android_mk.html
|
||||
# https://developer.android.com/ndk/guides/application_mk.html
|
||||
# if it makes sense for CMake, then replace LOCAL, APP, or NDK with ANDROID, and
|
||||
# we have that variable below.
|
||||
#
|
||||
# ANDROID_TOOLCHAIN
|
||||
# ANDROID_ABI
|
||||
# ANDROID_PLATFORM
|
||||
# ANDROID_STL
|
||||
# ANDROID_PIE
|
||||
# ANDROID_CPP_FEATURES
|
||||
# ANDROID_ALLOW_UNDEFINED_SYMBOLS
|
||||
# ANDROID_ARM_MODE
|
||||
# ANDROID_DISABLE_FORMAT_STRING_CHECKS
|
||||
# ANDROID_CCACHE
|
||||
# ANDROID_SANITIZE
|
||||
|
||||
cmake_minimum_required(VERSION 3.12.0)
|
||||
|
||||
# CMake invokes the toolchain file twice during the first build, but only once
|
||||
# during subsequent rebuilds. This was causing the various flags to be added
|
||||
# twice on the first build, and on a rebuild ninja would see only one set of the
|
||||
# flags and rebuild the world.
|
||||
# https://github.com/android-ndk/ndk/issues/323
|
||||
if(ANDROID_NDK_TOOLCHAIN_INCLUDED)
|
||||
return()
|
||||
endif(ANDROID_NDK_TOOLCHAIN_INCLUDED)
|
||||
set(ANDROID_NDK_TOOLCHAIN_INCLUDED true)
|
||||
|
||||
if(DEFINED ANDROID_USE_LEGACY_TOOLCHAIN_FILE)
|
||||
set(_USE_LEGACY_TOOLCHAIN_FILE ${ANDROID_USE_LEGACY_TOOLCHAIN_FILE})
|
||||
else()
|
||||
# Default to the legacy toolchain file to avoid changing the behavior of
|
||||
# CMAKE_CXX_FLAGS. See https://github.com/android/ndk/issues/1693.
|
||||
set(_USE_LEGACY_TOOLCHAIN_FILE true)
|
||||
endif()
|
||||
if(_USE_LEGACY_TOOLCHAIN_FILE)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/android-legacy.toolchain.cmake")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Android NDK path
|
||||
get_filename_component(ANDROID_NDK_EXPECTED_PATH
|
||||
"${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE)
|
||||
if(NOT ANDROID_NDK)
|
||||
set(CMAKE_ANDROID_NDK "${ANDROID_NDK_EXPECTED_PATH}")
|
||||
else()
|
||||
# Allow the user to specify their own NDK path, but emit a warning. This is an
|
||||
# uncommon use case, but helpful if users want to use a bleeding edge
|
||||
# toolchain file with a stable NDK.
|
||||
# https://github.com/android-ndk/ndk/issues/473
|
||||
get_filename_component(ANDROID_NDK "${ANDROID_NDK}" ABSOLUTE)
|
||||
if(NOT "${ANDROID_NDK}" STREQUAL "${ANDROID_NDK_EXPECTED_PATH}")
|
||||
message(WARNING "Using custom NDK path (ANDROID_NDK is set): ${ANDROID_NDK}")
|
||||
endif()
|
||||
set(CMAKE_ANDROID_NDK ${ANDROID_NDK})
|
||||
endif()
|
||||
unset(ANDROID_NDK_EXPECTED_PATH)
|
||||
file(TO_CMAKE_PATH "${CMAKE_ANDROID_NDK}" CMAKE_ANDROID_NDK)
|
||||
|
||||
# Android NDK revision
|
||||
# Possible formats:
|
||||
# * r16, build 1234: 16.0.1234
|
||||
# * r16b, build 1234: 16.1.1234
|
||||
# * r16 beta 1, build 1234: 16.0.1234-beta1
|
||||
#
|
||||
# Canary builds are not specially marked.
|
||||
file(READ "${CMAKE_ANDROID_NDK}/source.properties" ANDROID_NDK_SOURCE_PROPERTIES)
|
||||
|
||||
set(ANDROID_NDK_REVISION_REGEX
|
||||
"^Pkg\\.Desc = Android NDK\nPkg\\.Revision = ([0-9]+)\\.([0-9]+)\\.([0-9]+)(-beta([0-9]+))?")
|
||||
if(NOT ANDROID_NDK_SOURCE_PROPERTIES MATCHES "${ANDROID_NDK_REVISION_REGEX}")
|
||||
message(SEND_ERROR "Failed to parse Android NDK revision: ${CMAKE_ANDROID_NDK}/source.properties.\n${ANDROID_NDK_SOURCE_PROPERTIES}")
|
||||
endif()
|
||||
|
||||
set(ANDROID_NDK_MAJOR "${CMAKE_MATCH_1}")
|
||||
set(ANDROID_NDK_MINOR "${CMAKE_MATCH_2}")
|
||||
set(ANDROID_NDK_BUILD "${CMAKE_MATCH_3}")
|
||||
set(ANDROID_NDK_BETA "${CMAKE_MATCH_5}")
|
||||
if(ANDROID_NDK_BETA STREQUAL "")
|
||||
set(ANDROID_NDK_BETA "0")
|
||||
endif()
|
||||
set(ANDROID_NDK_REVISION
|
||||
"${ANDROID_NDK_MAJOR}.${ANDROID_NDK_MINOR}.${ANDROID_NDK_BUILD}${CMAKE_MATCH_4}")
|
||||
|
||||
# Touch toolchain variable to suppress "unused variable" warning.
|
||||
# This happens if CMake is invoked with the same command line the second time.
|
||||
if(CMAKE_TOOLCHAIN_FILE)
|
||||
endif()
|
||||
|
||||
# Determine the ABI.
|
||||
if(NOT CMAKE_ANDROID_ARCH_ABI)
|
||||
if(ANDROID_ABI STREQUAL "armeabi-v7a with NEON")
|
||||
set(CMAKE_ANDROID_ARCH_ABI armeabi-v7a)
|
||||
elseif(ANDROID_ABI)
|
||||
set(CMAKE_ANDROID_ARCH_ABI ${ANDROID_ABI})
|
||||
elseif(ANDROID_TOOLCHAIN_NAME MATCHES "^arm-linux-androideabi-")
|
||||
set(CMAKE_ANDROID_ARCH_ABI armeabi-v7a)
|
||||
elseif(ANDROID_TOOLCHAIN_NAME MATCHES "^aarch64-linux-android-")
|
||||
set(CMAKE_ANDROID_ARCH_ABI arm64-v8a)
|
||||
elseif(ANDROID_TOOLCHAIN_NAME MATCHES "^x86-")
|
||||
set(CMAKE_ANDROID_ARCH_ABI x86)
|
||||
elseif(ANDROID_TOOLCHAIN_NAME MATCHES "^x86_64-")
|
||||
set(CMAKE_ANDROID_ARCH_ABI x86_64)
|
||||
elseif(ANDROID_TOOLCHAIN_NAME MATCHES "^riscv64-")
|
||||
set(CMAKE_ANDROID_ARCH_ABI riscv64)
|
||||
else()
|
||||
set(CMAKE_ANDROID_ARCH_ABI armeabi-v7a)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(DEFINED ANDROID_ARM_NEON AND NOT ANDROID_ARM_NEON)
|
||||
message(FATAL_ERROR "Disabling Neon is no longer supported")
|
||||
endif()
|
||||
|
||||
if(CMAKE_ANDROID_ARCH_ABI STREQUAL "armeabi-v7a")
|
||||
set(CMAKE_ANDROID_ARM_NEON TRUE)
|
||||
|
||||
if(NOT DEFINED CMAKE_ANDROID_ARM_MODE)
|
||||
if(DEFINED ANDROID_FORCE_ARM_BUILD)
|
||||
set(CMAKE_ANDROID_ARM_MODE ${ANDROID_FORCE_ARM_BUILD})
|
||||
elseif(DEFINED ANDROID_ARM_MODE)
|
||||
if(ANDROID_ARM_MODE STREQUAL "arm")
|
||||
set(CMAKE_ANDROID_ARM_MODE TRUE)
|
||||
elseif(ANDROID_ARM_MODE STREQUAL "thumb")
|
||||
set(CMAKE_ANDROID_ARM_MODE FALSE)
|
||||
else()
|
||||
message(FATAL_ERROR "Invalid Android ARM mode: ${ANDROID_ARM_MODE}.")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# PIE is supported on all currently supported Android releases, but it is not
|
||||
# supported with static executables, so we still provide ANDROID_PIE as an
|
||||
# escape hatch for those.
|
||||
if(NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
|
||||
if(DEFINED ANDROID_PIE)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ${ANDROID_PIE})
|
||||
elseif(DEFINED ANDROID_APP_PIE)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ${ANDROID_APP_PIE})
|
||||
else()
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Default values for configurable variables.
|
||||
if(NOT ANDROID_TOOLCHAIN)
|
||||
set(ANDROID_TOOLCHAIN clang)
|
||||
elseif(ANDROID_TOOLCHAIN STREQUAL gcc)
|
||||
message(FATAL_ERROR "GCC is no longer supported. See "
|
||||
"https://android.googlesource.com/platform/ndk/+/master/docs/ClangMigration.md.")
|
||||
endif()
|
||||
|
||||
if(ANDROID_NATIVE_API_LEVEL AND NOT ANDROID_PLATFORM)
|
||||
if(ANDROID_NATIVE_API_LEVEL MATCHES "^android-[0-9]+$")
|
||||
set(ANDROID_PLATFORM ${ANDROID_NATIVE_API_LEVEL})
|
||||
elseif(ANDROID_NATIVE_API_LEVEL MATCHES "^[0-9]+$")
|
||||
set(ANDROID_PLATFORM android-${ANDROID_NATIVE_API_LEVEL})
|
||||
endif()
|
||||
endif()
|
||||
include(${CMAKE_ANDROID_NDK}/build/cmake/adjust_api_level.cmake)
|
||||
adjust_api_level("${ANDROID_PLATFORM}" CMAKE_SYSTEM_VERSION)
|
||||
|
||||
if(NOT DEFINED CMAKE_ANDROID_STL_TYPE AND DEFINED ANDROID_STL)
|
||||
set(CMAKE_ANDROID_STL_TYPE ${ANDROID_STL})
|
||||
endif()
|
||||
|
||||
if("hwaddress" IN_LIST ANDROID_SANITIZE AND "${CMAKE_ANDROID_STL_TYPE}" STREQUAL "c++_static")
|
||||
message(FATAL_ERROR "\
|
||||
hwaddress does not support c++_static. Use system or c++_shared.")
|
||||
endif()
|
||||
|
||||
if("${CMAKE_ANDROID_STL_TYPE}" STREQUAL "gnustl_shared" OR
|
||||
"${CMAKE_ANDROID_STL_TYPE}" STREQUAL "gnustl_static" OR
|
||||
"${CMAKE_ANDROID_STL_TYPE}" STREQUAL "stlport_shared" OR
|
||||
"${CMAKE_ANDROID_STL_TYPE}" STREQUAL "stlport_static")
|
||||
message(FATAL_ERROR "\
|
||||
${CMAKE_ANDROID_STL_TYPE} is no longer supported. Please switch to either c++_shared \
|
||||
or c++_static. See https://developer.android.com/ndk/guides/cpp-support.html \
|
||||
for more information.")
|
||||
endif()
|
||||
|
||||
# Standard cross-compiling stuff.
|
||||
set(CMAKE_SYSTEM_NAME Android)
|
||||
|
||||
# STL.
|
||||
if(ANDROID_STL)
|
||||
set(CMAKE_ANDROID_STL_TYPE ${ANDROID_STL})
|
||||
endif()
|
||||
|
||||
if(NDK_CCACHE AND NOT ANDROID_CCACHE)
|
||||
set(ANDROID_CCACHE "${NDK_CCACHE}")
|
||||
endif()
|
||||
if(ANDROID_CCACHE)
|
||||
set(CMAKE_C_COMPILER_LAUNCHER "${ANDROID_CCACHE}")
|
||||
set(CMAKE_CXX_COMPILER_LAUNCHER "${ANDROID_CCACHE}")
|
||||
endif()
|
||||
|
||||
# Configuration specific flags.
|
||||
if(ANDROID_STL_FORCE_FEATURES AND NOT DEFINED ANDROID_CPP_FEATURES)
|
||||
set(ANDROID_CPP_FEATURES "rtti exceptions")
|
||||
endif()
|
||||
|
||||
if(ANDROID_CPP_FEATURES)
|
||||
separate_arguments(ANDROID_CPP_FEATURES)
|
||||
foreach(feature ${ANDROID_CPP_FEATURES})
|
||||
if(NOT ${feature} MATCHES "^(rtti|exceptions|no-rtti|no-exceptions)$")
|
||||
message(FATAL_ERROR "Invalid Android C++ feature: ${feature}.")
|
||||
endif()
|
||||
if(${feature} STREQUAL "rtti")
|
||||
set(CMAKE_ANDROID_RTTI TRUE)
|
||||
endif()
|
||||
if(${feature} STREQUAL "no-rtti")
|
||||
set(CMAKE_ANDROID_RTTI FALSE)
|
||||
endif()
|
||||
if(${feature} STREQUAL "exceptions")
|
||||
set(CMAKE_ANDROID_EXCEPTIONS TRUE)
|
||||
endif()
|
||||
if(${feature} STREQUAL "no-exceptions")
|
||||
set(CMAKE_ANDROID_EXCEPTIONS FALSE)
|
||||
endif()
|
||||
endforeach()
|
||||
string(REPLACE ";" " " ANDROID_CPP_FEATURES "${ANDROID_CPP_FEATURES}")
|
||||
endif()
|
||||
|
||||
# Export configurable variables for the try_compile() command.
|
||||
set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES
|
||||
ANDROID_ABI
|
||||
ANDROID_ALLOW_UNDEFINED_SYMBOLS
|
||||
ANDROID_ARM_MODE
|
||||
ANDROID_ARM_NEON
|
||||
ANDROID_CCACHE
|
||||
ANDROID_CPP_FEATURES
|
||||
ANDROID_DISABLE_FORMAT_STRING_CHECKS
|
||||
ANDROID_PIE
|
||||
ANDROID_PLATFORM
|
||||
ANDROID_STL
|
||||
ANDROID_TOOLCHAIN
|
||||
ANDROID_USE_LEGACY_TOOLCHAIN_FILE
|
||||
ANDROID_SANITIZE
|
||||
)
|
||||
|
||||
if(DEFINED ANDROID_NO_UNDEFINED AND NOT DEFINED ANDROID_ALLOW_UNDEFINED_SYMBOLS)
|
||||
if(ANDROID_NO_UNDEFINED)
|
||||
set(ANDROID_ALLOW_UNDEFINED_SYMBOLS FALSE)
|
||||
else()
|
||||
set(ANDROID_ALLOW_UNDEFINED_SYMBOLS TRUE)
|
||||
endif()
|
||||
endif()
|
||||
if(DEFINED ANDROID_SO_UNDEFINED AND NOT DEFINED ANDROID_ALLOW_UNDEFINED_SYMBOLS)
|
||||
set(ANDROID_ALLOW_UNDEFINED_SYMBOLS "${ANDROID_SO_UNDEFINED}")
|
||||
endif()
|
||||
|
||||
# Exports compatible variables defined in exports.cmake.
|
||||
set(_ANDROID_EXPORT_COMPATIBILITY_VARIABLES TRUE)
|
||||
|
||||
if(CMAKE_HOST_SYSTEM_NAME STREQUAL Linux)
|
||||
set(ANDROID_HOST_TAG linux-x86_64)
|
||||
elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL Darwin)
|
||||
set(ANDROID_HOST_TAG darwin-x86_64)
|
||||
elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL Windows)
|
||||
set(ANDROID_HOST_TAG windows-x86_64)
|
||||
endif()
|
||||
|
||||
# Toolchain.
|
||||
set(ANDROID_TOOLCHAIN_ROOT
|
||||
"${CMAKE_ANDROID_NDK}/toolchains/llvm/prebuilt/${ANDROID_HOST_TAG}")
|
||||
|
||||
# NB: This variable causes CMake to automatically pass --sysroot to the
|
||||
# toolchain. Studio currently relies on this to recognize Android builds. If
|
||||
# this variable is removed, ensure that flag is still passed.
|
||||
# TODO: Teach Studio to recognize Android builds based on --target.
|
||||
set(CMAKE_SYSROOT "${ANDROID_TOOLCHAIN_ROOT}/sysroot")
|
||||
4
Android/android-ndk-r27d/build/cmake/compiler_id.cmake
Normal file
@ -0,0 +1,4 @@
|
||||
# The file is automatically generated when the NDK is built.
|
||||
set(CMAKE_ASM_COMPILER_VERSION 18.0.4)
|
||||
set(CMAKE_C_COMPILER_VERSION 18.0.4)
|
||||
set(CMAKE_CXX_COMPILER_VERSION 18.0.4)
|
||||
81
Android/android-ndk-r27d/build/cmake/exports.cmake
Normal file
@ -0,0 +1,81 @@
|
||||
# Copyright (C) 2020 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Read-only variables for compatibility with the other toolchain file.
|
||||
# We'll keep these around for the existing projects that still use them.
|
||||
# TODO: All of the variables here have equivalents in the standard set of
|
||||
# cmake configurable variables, so we can remove these once most of our
|
||||
# users migrate to those variables.
|
||||
|
||||
# From legacy toolchain file.
|
||||
set(ANDROID_NDK "${CMAKE_ANDROID_NDK}")
|
||||
set(ANDROID_ABI "${CMAKE_ANDROID_ARCH_ABI}")
|
||||
set(ANDROID_COMPILER_IS_CLANG TRUE)
|
||||
set(ANDROID_PLATFORM "android-${CMAKE_SYSTEM_VERSION}")
|
||||
set(ANDROID_PLATFORM_LEVEL "${CMAKE_SYSTEM_VERSION}")
|
||||
set(ANDROID_ARM_NEON TRUE)
|
||||
if(CMAKE_ANDROID_ARM_MODE)
|
||||
set(ANDROID_ARM_MODE "arm")
|
||||
set(ANDROID_FORCE_ARM_BUILD TRUE)
|
||||
else()
|
||||
set(ANDROID_ARM_MODE "thumb")
|
||||
endif()
|
||||
set(ANDROID_ARCH_NAME "${CMAKE_ANDROID_ARCH}")
|
||||
set(ANDROID_LLVM_TRIPLE "${CMAKE_ANDROID_ARCH_LLVM_TRIPLE}${CMAKE_SYSTEM_VERSION}")
|
||||
set(ANDROID_TOOLCHAIN_ROOT "${CMAKE_ANDROID_NDK_TOOLCHAIN_UNIFIED}")
|
||||
set(ANDROID_HOST_TAG "${CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG}")
|
||||
set(ANDROID_HOST_PREBUILTS "${CMAKE_ANDROID_NDK}/prebuilt/${CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG}")
|
||||
set(ANDROID_AR "${CMAKE_AR}")
|
||||
set(ANDROID_RANLIB "${CMAKE_RANLIB}")
|
||||
set(ANDROID_STRIP "${CMAKE_STRIP}")
|
||||
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
|
||||
set(ANDROID_TOOLCHAIN_SUFFIX ".exe")
|
||||
endif()
|
||||
|
||||
# From other toolchain file.
|
||||
set(ANDROID_NATIVE_API_LEVEL "${ANDROID_PLATFORM_LEVEL}")
|
||||
if(ANDROID_ALLOW_UNDEFINED_SYMBOLS)
|
||||
set(ANDROID_SO_UNDEFINED TRUE)
|
||||
else()
|
||||
set(ANDROID_NO_UNDEFINED TRUE)
|
||||
endif()
|
||||
set(ANDROID_FUNCTION_LEVEL_LINKING TRUE)
|
||||
set(ANDROID_GOLD_LINKER TRUE)
|
||||
set(ANDROID_NOEXECSTACK TRUE)
|
||||
set(ANDROID_RELRO TRUE)
|
||||
if(ANDROID_CPP_FEATURES MATCHES "rtti"
|
||||
AND ANDROID_CPP_FEATURES MATCHES "exceptions")
|
||||
set(ANDROID_STL_FORCE_FEATURES TRUE)
|
||||
endif()
|
||||
if(ANDROID_CCACHE)
|
||||
set(NDK_CCACHE "${ANDROID_CCACHE}")
|
||||
endif()
|
||||
set(ANDROID_NDK_HOST_X64 TRUE)
|
||||
set(ANDROID_NDK_LAYOUT RELEASE)
|
||||
if(CMAKE_ANDROID_ARCH_ABI STREQUAL "armeabi-v7a")
|
||||
set(ARMEABI_V7A TRUE)
|
||||
set(NEON TRUE)
|
||||
elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a")
|
||||
set(ARM64_V8A TRUE)
|
||||
elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL "x86")
|
||||
set(X86 TRUE)
|
||||
elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL "x86_64")
|
||||
set(X86_64 TRUE)
|
||||
elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL "riscv64")
|
||||
set(RISCV64 TRUE)
|
||||
endif()
|
||||
set(ANDROID_NDK_HOST_SYSTEM_NAME "${ANDROID_HOST_TAG}")
|
||||
set(ANDROID_NDK_ABI_NAME "${CMAKE_ANDROID_ARCH_ABI}")
|
||||
set(ANDROID_NDK_RELEASE "r${ANDROID_NDK_REVISION}")
|
||||
set(TOOL_OS_SUFFIX "${ANDROID_TOOLCHAIN_SUFFIX}")
|
||||
121
Android/android-ndk-r27d/build/cmake/flags.cmake
Normal file
@ -0,0 +1,121 @@
|
||||
# Copyright (C) 2020 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This file will be included directly by cmake. It is used to provide
|
||||
# additional cflags / ldflags.
|
||||
|
||||
cmake_minimum_required(VERSION 3.12.0)
|
||||
|
||||
set(_ANDROID_NDK_INIT_CFLAGS)
|
||||
set(_ANDROID_NDK_INIT_CFLAGS_DEBUG)
|
||||
set(_ANDROID_NDK_INIT_CFLAGS_RELEASE)
|
||||
set(_ANDROID_NDK_INIT_LDFLAGS)
|
||||
set(_ANDROID_NDK_INIT_LDFLAGS_EXE)
|
||||
|
||||
# Generic flags.
|
||||
string(APPEND _ANDROID_NDK_INIT_CFLAGS
|
||||
" -DANDROID"
|
||||
" -fdata-sections"
|
||||
" -ffunction-sections"
|
||||
" -funwind-tables"
|
||||
" -fstack-protector-strong"
|
||||
" -no-canonical-prefixes")
|
||||
|
||||
if(ANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES)
|
||||
string(APPEND _ANDROID_NDK_INIT_CFLAGS " -D__BIONIC_NO_PAGE_SIZE_MACRO")
|
||||
if(ANDROID_ABI STREQUAL arm64-v8a OR ANDROID_ABI STREQUAL x86_64)
|
||||
string(APPEND _ANDROID_NDK_INIT_LDFLAGS " -Wl,-z,max-page-size=16384")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ANDROID_WEAK_API_DEFS)
|
||||
string(APPEND _ANDROID_NDK_INIT_CFLAGS
|
||||
" -D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__"
|
||||
" -Werror=unguarded-availability")
|
||||
endif()
|
||||
|
||||
if("hwaddress" IN_LIST ANDROID_SANITIZE)
|
||||
string(APPEND _ANDROID_NDK_INIT_CFLAGS " -fsanitize=hwaddress -fno-omit-frame-pointer")
|
||||
string(APPEND _ANDROID_NDK_INIT_LDFLAGS " -fsanitize=hwaddress")
|
||||
endif()
|
||||
|
||||
if("memtag" IN_LIST ANDROID_SANITIZE)
|
||||
string(APPEND _ANDROID_NDK_INIT_CFLAGS " -fsanitize=memtag-stack -fno-omit-frame-pointer")
|
||||
string(APPEND _ANDROID_NDK_INIT_LDFLAGS " -fsanitize=memtag-stack,memtag-heap -fsanitize-memtag-mode=sync")
|
||||
if(CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a")
|
||||
string(APPEND _ANDROID_NDK_INIT_CFLAGS " -march=armv8-a+memtag")
|
||||
string(APPEND _ANDROID_NDK_INIT_LDFLAGS " -march=armv8-a+memtag")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
string(APPEND _ANDROID_NDK_INIT_CFLAGS_DEBUG " -fno-limit-debug-info")
|
||||
|
||||
# If we're using LLD we need to use a slower build-id algorithm to work around
|
||||
# the old version of LLDB in Android Studio, which doesn't understand LLD's
|
||||
# default hash ("fast").
|
||||
#
|
||||
# https://github.com/android/ndk/issues/885
|
||||
string(APPEND _ANDROID_NDK_INIT_LDFLAGS " -Wl,--build-id=sha1")
|
||||
|
||||
if(CMAKE_SYSTEM_VERSION LESS 30)
|
||||
# https://github.com/android/ndk/issues/1196
|
||||
# https://github.com/android/ndk/issues/1589
|
||||
string(APPEND _ANDROID_NDK_INIT_LDFLAGS " -Wl,--no-rosegment")
|
||||
endif()
|
||||
|
||||
if (NOT ANDROID_ALLOW_UNDEFINED_VERSION_SCRIPT_SYMBOLS)
|
||||
string(APPEND _ANDROID_NDK_INIT_LDFLAGS " -Wl,--no-undefined-version")
|
||||
endif()
|
||||
|
||||
string(APPEND _ANDROID_NDK_INIT_LDFLAGS " -Wl,--fatal-warnings")
|
||||
# This should only be set for release modes, but CMake doesn't provide a way for
|
||||
# us to be that specific in the new toolchain file.
|
||||
# https://github.com/android/ndk/issues/1813
|
||||
string(APPEND _ANDROID_NDK_INIT_LDFLAGS " -Wl,--gc-sections")
|
||||
string(APPEND _ANDROID_NDK_INIT_LDFLAGS_EXE " -Wl,--gc-sections")
|
||||
|
||||
# Toolchain and ABI specific flags.
|
||||
if(CMAKE_ANDROID_ARCH_ABI STREQUAL x86 AND CMAKE_SYSTEM_VERSION LESS 24)
|
||||
# http://b.android.com/222239
|
||||
# http://b.android.com/220159 (internal http://b/31809417)
|
||||
# x86 devices have stack alignment issues.
|
||||
string(APPEND _ANDROID_NDK_INIT_CFLAGS " -mstackrealign")
|
||||
endif()
|
||||
|
||||
string(APPEND _ANDROID_NDK_INIT_CFLAGS " -D_FORTIFY_SOURCE=2")
|
||||
|
||||
if(CMAKE_ANDROID_ARCH_ABI MATCHES "armeabi")
|
||||
# Clang does not set this up properly when using -fno-integrated-as.
|
||||
# https://github.com/android-ndk/ndk/issues/906
|
||||
string(APPEND _ANDROID_NDK_INIT_CFLAGS " -march=armv7-a")
|
||||
if(NOT CMAKE_ANDROID_ARM_MODE)
|
||||
string(APPEND _ANDROID_NDK_INIT_CFLAGS " -mthumb")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# CMake automatically forwards all compiler flags to the linker, and clang
|
||||
# doesn't like having -Wa flags being used for linking. To prevent CMake from
|
||||
# doing this would require meddling with the CMAKE_<LANG>_COMPILE_OBJECT rules,
|
||||
# which would get quite messy.
|
||||
string(APPEND _ANDROID_NDK_INIT_LDFLAGS " -Qunused-arguments")
|
||||
|
||||
if(ANDROID_DISABLE_FORMAT_STRING_CHECKS)
|
||||
string(APPEND _ANDROID_NDK_INIT_CFLAGS " -Wno-error=format-security")
|
||||
else()
|
||||
string(APPEND _ANDROID_NDK_INIT_CFLAGS " -Wformat -Werror=format-security")
|
||||
endif()
|
||||
|
||||
if(NOT ANDROID_ALLOW_UNDEFINED_SYMBOLS)
|
||||
string(APPEND _ANDROID_NDK_INIT_LDFLAGS " -Wl,--no-undefined")
|
||||
endif()
|
||||
@ -0,0 +1,16 @@
|
||||
# Copyright (C) 2020 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This is a hook file that will be included by cmake at the end of
|
||||
# Modules/Platform/Android-Clang.cmake.
|
||||
@ -0,0 +1,22 @@
|
||||
# Copyright (C) 2020 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This is a hook file that will be included by cmake at the end of
|
||||
# Modules/Platform/Android-Determine.cmake.
|
||||
|
||||
# android.toolchain.cmake may set this to export old variables.
|
||||
if(_ANDROID_EXPORT_COMPATIBILITY_VARIABLES)
|
||||
file(READ "${CMAKE_ANDROID_NDK}/build/cmake/exports.cmake" _EXPORTS)
|
||||
string(APPEND CMAKE_SYSTEM_CUSTOM_CODE "\n${_EXPORTS}\n")
|
||||
endif()
|
||||
@ -0,0 +1,16 @@
|
||||
# Copyright (C) 2020 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This is a hook file that will be included by cmake at the end of
|
||||
# Modules/Platform/Android-Initialize.cmake.
|
||||
@ -0,0 +1,16 @@
|
||||
# Copyright (C) 2020 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This is a hook file that will be included by cmake at the end of
|
||||
# Modules/Platform/Android.cmake.
|
||||
@ -0,0 +1,16 @@
|
||||
# Copyright (C) 2020 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This is a hook file that will be included by cmake at the end of
|
||||
# Modules/Platform/Android/Determine-Compiler.cmake.
|
||||
@ -0,0 +1,16 @@
|
||||
# Copyright (C) 2020 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This is a hook file that will be included by cmake at the beginning of
|
||||
# Modules/Platform/Android-Clang.cmake.
|
||||
@ -0,0 +1,16 @@
|
||||
# Copyright (C) 2020 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This is a hook file that will be included by cmake at the beginning of
|
||||
# Modules/Platform/Android-Determine.cmake.
|
||||
@ -0,0 +1,16 @@
|
||||
# Copyright (C) 2020 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This is a hook file that will be included by cmake at the beginning of
|
||||
# Modules/Platform/Android-Initialize.cmake.
|
||||
16
Android/android-ndk-r27d/build/cmake/hooks/pre/Android.cmake
Normal file
@ -0,0 +1,16 @@
|
||||
# Copyright (C) 2020 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This is a hook file that will be included by cmake at the beginning of
|
||||
# Modules/Platform/Android.cmake.
|
||||
@ -0,0 +1,42 @@
|
||||
# Copyright (C) 2020 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This is a hook file that will be included by cmake at the beginning of
|
||||
# Modules/Platform/Android/Determine-Compiler.cmake.
|
||||
|
||||
# Skip hook for the legacy toolchain workflow.
|
||||
if(CMAKE_SYSTEM_VERSION EQUAL 1)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(${CMAKE_VERSION} VERSION_LESS "3.22.0")
|
||||
# If we don't explicitly set the target CMake will ID the compiler using the
|
||||
# default target, causing MINGW to be defined when a Windows host is used.
|
||||
# https://github.com/android/ndk/issues/1581
|
||||
# https://gitlab.kitware.com/cmake/cmake/-/issues/22647
|
||||
if(CMAKE_ANDROID_ARCH_ABI STREQUAL armeabi-v7a)
|
||||
set(ANDROID_LLVM_TRIPLE armv7-none-linux-androideabi)
|
||||
elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL arm64-v8a)
|
||||
set(ANDROID_LLVM_TRIPLE aarch64-none-linux-android)
|
||||
elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL x86)
|
||||
set(ANDROID_LLVM_TRIPLE i686-none-linux-android)
|
||||
elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL x86_64)
|
||||
set(ANDROID_LLVM_TRIPLE x86_64-none-linux-android)
|
||||
else()
|
||||
message(FATAL_ERROR "Invalid Android ABI: ${ANDROID_ABI}.")
|
||||
endif()
|
||||
set(CMAKE_ASM_COMPILER_TARGET "${ANDROID_LLVM_TRIPLE}${CMAKE_SYSTEM_VERSION}")
|
||||
set(CMAKE_C_COMPILER_TARGET "${ANDROID_LLVM_TRIPLE}${CMAKE_SYSTEM_VERSION}")
|
||||
set(CMAKE_CXX_COMPILER_TARGET "${ANDROID_LLVM_TRIPLE}${CMAKE_SYSTEM_VERSION}")
|
||||
endif()
|
||||
23
Android/android-ndk-r27d/build/cmake/platforms.cmake
Normal file
@ -0,0 +1,23 @@
|
||||
set(NDK_MIN_PLATFORM_LEVEL "21")
|
||||
set(NDK_MAX_PLATFORM_LEVEL "35")
|
||||
set(NDK_PLATFORM_ALIAS_20 "android-19")
|
||||
set(NDK_PLATFORM_ALIAS_25 "android-24")
|
||||
set(NDK_PLATFORM_ALIAS_J "android-16")
|
||||
set(NDK_PLATFORM_ALIAS_J-MR1 "android-17")
|
||||
set(NDK_PLATFORM_ALIAS_J-MR2 "android-18")
|
||||
set(NDK_PLATFORM_ALIAS_K "android-19")
|
||||
set(NDK_PLATFORM_ALIAS_L "android-21")
|
||||
set(NDK_PLATFORM_ALIAS_L-MR1 "android-22")
|
||||
set(NDK_PLATFORM_ALIAS_M "android-23")
|
||||
set(NDK_PLATFORM_ALIAS_N "android-24")
|
||||
set(NDK_PLATFORM_ALIAS_N-MR1 "android-24")
|
||||
set(NDK_PLATFORM_ALIAS_O "android-26")
|
||||
set(NDK_PLATFORM_ALIAS_O-MR1 "android-27")
|
||||
set(NDK_PLATFORM_ALIAS_P "android-28")
|
||||
set(NDK_PLATFORM_ALIAS_Q "android-29")
|
||||
set(NDK_PLATFORM_ALIAS_R "android-30")
|
||||
set(NDK_PLATFORM_ALIAS_S "android-31")
|
||||
set(NDK_PLATFORM_ALIAS_Sv2 "android-32")
|
||||
set(NDK_PLATFORM_ALIAS_Tiramisu "android-33")
|
||||
set(NDK_PLATFORM_ALIAS_UpsideDownCake "android-34")
|
||||
set(NDK_PLATFORM_ALIAS_VanillaIceCream "android-35")
|
||||
1
Android/android-ndk-r27d/build/cmake/system_libs.cmake
Normal file
@ -0,0 +1 @@
|
||||
set(NDK_SYSTEM_LIBS "libEGL.so;libGLESv1_CM.so;libGLESv2.so;libGLESv3.so;libOpenMAXAL.so;libOpenSLES.so;libaaudio.so;libamidi.so;libandroid.so;libbinder_ndk.so;libc.so;libcamera2ndk.so;libdl.so;libicu.so;libjnigraphics.so;liblog.so;libm.so;libmediandk.so;libnativehelper.so;libnativewindow.so;libneuralnetworks.so;libstdc++.so;libsync.so;libvulkan.so;libz.so")
|
||||
40
Android/android-ndk-r27d/build/core/abis.mk
Normal file
@ -0,0 +1,40 @@
|
||||
NDK_DEFAULT_ABIS := arm64-v8a armeabi-v7a x86 x86_64
|
||||
NDK_DEPRECATED_ABIS :=
|
||||
NDK_KNOWN_DEVICE_ABI32S := armeabi-v7a x86
|
||||
NDK_KNOWN_DEVICE_ABI64S := arm64-v8a riscv64 x86_64
|
||||
NDK_KNOWN_DEVICE_ABIS := arm64-v8a armeabi-v7a riscv64 x86 x86_64
|
||||
NDK_ABI_armeabi-v7a_PROC := armv7-a
|
||||
NDK_ABI_armeabi-v7a_ARCH := arm
|
||||
NDK_ABI_armeabi-v7a_TRIPLE := arm-linux-androideabi
|
||||
NDK_ABI_armeabi-v7a_LLVM_TRIPLE := armv7-none-linux-androideabi
|
||||
NDK_ABI_armeabi-v7a_MIN_OS_VERSION := 21
|
||||
NDK_PROC_armv7-a_ABI := armeabi-v7a
|
||||
NDK_ARCH_arm_ABI := armeabi-v7a
|
||||
NDK_ABI_arm64-v8a_PROC := aarch64
|
||||
NDK_ABI_arm64-v8a_ARCH := arm64
|
||||
NDK_ABI_arm64-v8a_TRIPLE := aarch64-linux-android
|
||||
NDK_ABI_arm64-v8a_LLVM_TRIPLE := aarch64-none-linux-android
|
||||
NDK_ABI_arm64-v8a_MIN_OS_VERSION := 21
|
||||
NDK_PROC_aarch64_ABI := arm64-v8a
|
||||
NDK_ARCH_arm64_ABI := arm64-v8a
|
||||
NDK_ABI_riscv64_PROC := riscv64
|
||||
NDK_ABI_riscv64_ARCH := riscv64
|
||||
NDK_ABI_riscv64_TRIPLE := riscv64-linux-android
|
||||
NDK_ABI_riscv64_LLVM_TRIPLE := riscv64-none-linux-android
|
||||
NDK_ABI_riscv64_MIN_OS_VERSION := 35
|
||||
NDK_PROC_riscv64_ABI := riscv64
|
||||
NDK_ARCH_riscv64_ABI := riscv64
|
||||
NDK_ABI_x86_PROC := i686
|
||||
NDK_ABI_x86_ARCH := x86
|
||||
NDK_ABI_x86_TRIPLE := i686-linux-android
|
||||
NDK_ABI_x86_LLVM_TRIPLE := i686-none-linux-android
|
||||
NDK_ABI_x86_MIN_OS_VERSION := 21
|
||||
NDK_PROC_i686_ABI := x86
|
||||
NDK_ARCH_x86_ABI := x86
|
||||
NDK_ABI_x86_64_PROC := x86_64
|
||||
NDK_ABI_x86_64_ARCH := x86_64
|
||||
NDK_ABI_x86_64_TRIPLE := x86_64-linux-android
|
||||
NDK_ABI_x86_64_LLVM_TRIPLE := x86_64-none-linux-android
|
||||
NDK_ABI_x86_64_MIN_OS_VERSION := 21
|
||||
NDK_PROC_x86_64_ABI := x86_64
|
||||
NDK_ARCH_x86_64_ABI := x86_64
|
||||
247
Android/android-ndk-r27d/build/core/add-application.mk
Normal file
@ -0,0 +1,247 @@
|
||||
# Copyright (C) 2009 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# this script is used to record an application definition in the
|
||||
# NDK build system, before performing any build whatsoever.
|
||||
#
|
||||
# It is included repeatedly from build/core/main.mk and expects a
|
||||
# variable named '_application_mk' which points to a given Application.mk
|
||||
# file that will be included here. The latter must define a few variables
|
||||
# to describe the application to the build system, and the rest of the
|
||||
# code here will perform book-keeping and basic checks
|
||||
#
|
||||
|
||||
$(call assert-defined, _application_mk _app)
|
||||
$(call ndk_log,Parsing $(_application_mk))
|
||||
|
||||
$(call clear-vars, $(NDK_APP_VARS))
|
||||
|
||||
# Check that NDK_DEBUG is properly defined. If it is
|
||||
# the only valid states are: undefined, 0, 1, false and true
|
||||
#
|
||||
# We set APP_DEBUG to <undefined>, 'true' or 'false'.
|
||||
#
|
||||
APP_DEBUG := $(strip $(NDK_DEBUG))
|
||||
ifeq ($(APP_DEBUG),0)
|
||||
APP_DEBUG:= false
|
||||
endif
|
||||
ifeq ($(APP_DEBUG),1)
|
||||
APP_DEBUG := true
|
||||
endif
|
||||
ifdef APP_DEBUG
|
||||
ifneq (,$(filter-out true false,$(APP_DEBUG)))
|
||||
$(call __ndk_warning,NDK_DEBUG is defined to the unsupported value '$(NDK_DEBUG)', will be ignored!)
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(_application_mk)
|
||||
|
||||
$(call check-required-vars,$(NDK_APP_VARS_REQUIRED),$(_application_mk))
|
||||
|
||||
_map := NDK_APP.$(_app)
|
||||
|
||||
# strip the 'lib' prefix in front of APP_MODULES modules
|
||||
APP_MODULES := $(call strip-lib-prefix,$(APP_MODULES))
|
||||
|
||||
APP_PROJECT_PATH := $(strip $(APP_PROJECT_PATH))
|
||||
ifndef APP_PROJECT_PATH
|
||||
APP_PROJECT_PATH := $(NDK_PROJECT_PATH)
|
||||
endif
|
||||
|
||||
include $(BUILD_SYSTEM)/setup-app-platform.mk
|
||||
|
||||
# Check that the value of APP_ABI corresponds to known ABIs
|
||||
# 'all' is a special case that means 'all supported ABIs'
|
||||
#
|
||||
# It will be handled in setup-app.mk. We can't hope to change
|
||||
# the value of APP_ABI is the user enforces it on the command-line
|
||||
# with a call like: ndk-build APP_ABI=all
|
||||
#
|
||||
# Because GNU Make makes the APP_ABI variable read-only (any assignments
|
||||
# to it will be ignored)
|
||||
#
|
||||
APP_ABI := $(subst $(comma),$(space),$(strip $(APP_ABI)))
|
||||
ifndef APP_ABI
|
||||
APP_ABI := $(NDK_DEFAULT_ABIS)
|
||||
endif
|
||||
|
||||
# If APP_BUILD_SCRIPT is defined, check that the file exists.
|
||||
# If undefined, look in $(APP_PROJECT_PATH)/jni/Android.mk
|
||||
#
|
||||
APP_BUILD_SCRIPT := $(strip $(APP_BUILD_SCRIPT))
|
||||
ifdef APP_BUILD_SCRIPT
|
||||
_build_script := $(strip $(wildcard $(APP_BUILD_SCRIPT)))
|
||||
ifndef _build_script
|
||||
$(call __ndk_info,Your APP_BUILD_SCRIPT points to an unknown file: $(APP_BUILD_SCRIPT))
|
||||
$(call __ndk_error,Aborting...)
|
||||
endif
|
||||
APP_BUILD_SCRIPT := $(_build_script)
|
||||
$(call ndk_log, Using build script $(APP_BUILD_SCRIPT))
|
||||
else
|
||||
ifeq (null,$(APP_PROJECT_PATH))
|
||||
$(call __ndk_info,NDK_PROJECT_PATH==null. Please explicitly set APP_BUILD_SCRIPT.)
|
||||
$(call __ndk_error,Aborting.)
|
||||
endif
|
||||
|
||||
_build_script := $(strip $(wildcard $(APP_PROJECT_PATH)/jni/Android.mk))
|
||||
ifndef _build_script
|
||||
$(call __ndk_info,There is no Android.mk under $(APP_PROJECT_PATH)/jni)
|
||||
$(call __ndk_info,If this is intentional, please define APP_BUILD_SCRIPT to point)
|
||||
$(call __ndk_info,to a valid NDK build script.)
|
||||
$(call __ndk_error,Aborting...)
|
||||
endif
|
||||
APP_BUILD_SCRIPT := $(_build_script)
|
||||
$(call ndk_log, Defaulted to APP_BUILD_SCRIPT=$(APP_BUILD_SCRIPT))
|
||||
endif
|
||||
|
||||
# Determine whether the application should be debuggable.
|
||||
# - If APP_DEBUG is set to 'true', then it always should.
|
||||
# - If APP_DEBUG is set to 'false', then it never should
|
||||
# - Otherwise, extract the android:debuggable attribute from the manifest.
|
||||
#
|
||||
ifdef APP_DEBUG
|
||||
APP_DEBUGGABLE := $(APP_DEBUG)
|
||||
ifeq ($(NDK_LOG),1)
|
||||
ifeq ($(APP_DEBUG),true)
|
||||
$(call ndk_log,Application '$(_app)' forced debuggable through NDK_DEBUG)
|
||||
else
|
||||
$(call ndk_log,Application '$(_app)' forced *not* debuggable through NDK_DEBUG)
|
||||
endif
|
||||
endif
|
||||
else
|
||||
# NOTE: To make unit-testing simpler, handle the case where there is no manifest.
|
||||
APP_DEBUGGABLE := false
|
||||
ifdef APP_MANIFEST
|
||||
APP_DEBUGGABLE := $(shell $(HOST_PYTHON) $(BUILD_PY)/extract_manifest.py debuggable $(call host-path,$(APP_MANIFEST)))
|
||||
endif
|
||||
ifeq ($(NDK_LOG),1)
|
||||
ifeq ($(APP_DEBUGGABLE),true)
|
||||
$(call ndk_log,Application '$(_app)' *is* debuggable)
|
||||
else
|
||||
$(call ndk_log,Application '$(_app)' is not debuggable)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
# LOCAL_BUILD_MODE will be either release or debug
|
||||
#
|
||||
# If APP_OPTIM is defined in the Application.mk, just use this.
|
||||
#
|
||||
# Otherwise, set to 'debug' if android:debuggable is set to TRUE,
|
||||
# and to 'release' if not.
|
||||
#
|
||||
ifneq ($(APP_OPTIM),)
|
||||
# check that APP_OPTIM, if defined, is either 'release' or 'debug'
|
||||
$(if $(filter-out release debug,$(APP_OPTIM)),\
|
||||
$(call __ndk_info, The APP_OPTIM defined in $(_application_mk) must only be 'release' or 'debug')\
|
||||
$(call __ndk_error,Aborting)\
|
||||
)
|
||||
$(call ndk_log,Selecting optimization mode through Application.mk: $(APP_OPTIM))
|
||||
else
|
||||
ifeq ($(APP_DEBUGGABLE),true)
|
||||
$(call ndk_log,Selecting debug optimization mode (app is debuggable))
|
||||
APP_OPTIM := debug
|
||||
else
|
||||
$(call ndk_log,Selecting release optimization mode (app is not debuggable))
|
||||
APP_OPTIM := release
|
||||
endif
|
||||
endif
|
||||
|
||||
APP_CFLAGS := $(strip $(APP_CFLAGS))
|
||||
APP_CONLYFLAGS := $(strip $(APP_CONLYFLAGS))
|
||||
APP_CPPFLAGS := $(strip $(APP_CPPFLAGS))
|
||||
APP_CXXFLAGS := $(strip $(APP_CXXFLAGS))
|
||||
APP_ASFLAGS := $(strip $(APP_ASFLAGS))
|
||||
APP_ASMFLAGS := $(strip $(APP_ASMFLAGS))
|
||||
APP_LDFLAGS := $(strip $(APP_LDFLAGS))
|
||||
|
||||
# Check that APP_STL is defined. If not, use the default value (system)
|
||||
# otherwise, check that the name is correct.
|
||||
APP_STL := $(strip $(APP_STL))
|
||||
ifndef APP_STL
|
||||
APP_STL := system
|
||||
else
|
||||
ifneq ($(filter $(APP_STL),gnustl_static gnustl_shared stlport_static stlport_shared),)
|
||||
$(call __ndk_error,APP_STL $(APP_STL) is no longer supported. Please \
|
||||
switch to either c++_static or c++_shared. See \
|
||||
https://developer.android.com/ndk/guides/cpp-support.html for more \
|
||||
information.)
|
||||
endif
|
||||
$(call ndk-stl-check,$(APP_STL))
|
||||
endif
|
||||
|
||||
# wrap.sh files can be specified in the user's Application.mk in either an
|
||||
# ABI-generic (APP_WRAP_SH) or ABI-specific (APP_WRAP_SH_x86, etc) fashion.
|
||||
# These two approaches cannot be combined; if any ABI-specific wrap.sh files are
|
||||
# specified then it is an error to also specify an ABI-generic one.
|
||||
#
|
||||
# After this block, only the ABI-specific values should be checked; if there is
|
||||
# an ABI-generic script specified the ABI-specific variables will be populated
|
||||
# with the generic script.
|
||||
NDK_NO_USER_WRAP_SH := true
|
||||
ifneq ($(APP_WRAP_SH),)
|
||||
NDK_NO_USER_WRAP_SH := false
|
||||
endif
|
||||
|
||||
NDK_HAVE_ABI_SPECIFIC_WRAP_SH := false
|
||||
$(foreach _abi,$(NDK_ALL_ABIS),\
|
||||
$(if $(APP_WRAP_SH_$(_abi)),\
|
||||
$(eval NDK_HAVE_ABI_SPECIFIC_WRAP_SH := true)))
|
||||
|
||||
ifeq ($(NDK_HAVE_ABI_SPECIFIC_WRAP_SH),true)
|
||||
# It is an error to have both ABI-specific and ABI-generic wrap.sh files
|
||||
# specified.
|
||||
ifneq ($(APP_WRAP_SH),)
|
||||
$(call __ndk_error,Found both ABI-specific and ABI-generic APP_WRAP_SH \
|
||||
directives. Must use either all ABI-specific or only ABI-generic.)
|
||||
endif
|
||||
NDK_NO_USER_WRAP_SH := false
|
||||
else
|
||||
# If we have no ABI-specific wrap.sh files but we *do* have an ABI-generic
|
||||
# one, install the generic one for all ABIs.
|
||||
$(foreach _abi,$(NDK_ALL_ABIS),\
|
||||
$(eval APP_WRAP_SH_$(_abi) := $(APP_WRAP_SH)))
|
||||
endif
|
||||
|
||||
# Stripping can be configured both at the app (APP_STRIP_MODE) and module level
|
||||
# (LOCAL_STRIP_MODE). The module setting always overrides the application
|
||||
# setting.
|
||||
#
|
||||
# This value is passed as-is as the flag to the strip command except when it is
|
||||
# set to the special value "none". If set to "none", the binary will not be
|
||||
# stripped at all.
|
||||
ifeq ($(APP_STRIP_MODE),)
|
||||
# The strip command is only used for shared libraries and executables. It is
|
||||
# thus safe to use --strip-unneeded, which is only dangerous when applied to
|
||||
# static libraries or object files.
|
||||
APP_STRIP_MODE := --strip-unneeded
|
||||
endif
|
||||
|
||||
$(if $(call get,$(_map),defined),\
|
||||
$(call __ndk_info,Weird, the application $(_app) is already defined by $(call get,$(_map),defined))\
|
||||
$(call __ndk_error,Aborting)\
|
||||
)
|
||||
|
||||
$(call set,$(_map),defined,$(_application_mk))
|
||||
|
||||
# Record all app-specific variable definitions
|
||||
$(foreach __name,$(NDK_APP_VARS),\
|
||||
$(call set,$(_map),$(__name),$($(__name)))\
|
||||
)
|
||||
|
||||
# Record the Application.mk for debugging
|
||||
$(call set,$(_map),Application.mk,$(_application_mk))
|
||||
|
||||
NDK_ALL_APPS += $(_app)
|
||||
85
Android/android-ndk-r27d/build/core/add-toolchain.mk
Normal file
@ -0,0 +1,85 @@
|
||||
# Copyright (C) 2009 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# this script is included repeatedly by main.mk to add a new toolchain
|
||||
# definition to the NDK build system.
|
||||
#
|
||||
# '_config_mk' must be defined as the path of a toolchain
|
||||
# configuration file (config.mk) that will be included here.
|
||||
#
|
||||
$(call assert-defined, _config_mk)
|
||||
|
||||
# The list of variables that must or may be defined
|
||||
# by the toolchain configuration file
|
||||
#
|
||||
NDK_TOOLCHAIN_VARS_REQUIRED := TOOLCHAIN_ABIS TOOLCHAIN_ARCH
|
||||
NDK_TOOLCHAIN_VARS_OPTIONAL :=
|
||||
|
||||
# Clear variables that are supposed to be defined by the config file
|
||||
$(call clear-vars,$(NDK_TOOLCHAIN_VARS_REQUIRED))
|
||||
$(call clear-vars,$(NDK_TOOLCHAIN_VARS_OPTIONAL))
|
||||
|
||||
# Include the config file
|
||||
include $(_config_mk)
|
||||
|
||||
ifeq ($(TOOLCHAIN_ABIS)$(TOOLCHAIN_ARCH),)
|
||||
# Ignore if both TOOLCHAIN_ABIS and TOOLCHAIN_ARCH are not defined
|
||||
else
|
||||
|
||||
# Check that the proper variables were defined
|
||||
$(call check-required-vars,$(NDK_TOOLCHAIN_VARS_REQUIRED),$(_config_mk))
|
||||
|
||||
# Check that the file didn't do something stupid
|
||||
$(call assert-defined, _config_mk)
|
||||
|
||||
# Now record the toolchain-specific information
|
||||
_dir := $(patsubst %/,%,$(dir $(_config_mk)))
|
||||
_name := $(notdir $(_dir))
|
||||
_arch := $(TOOLCHAIN_ARCH)
|
||||
_abis := $(TOOLCHAIN_ABIS)
|
||||
|
||||
_toolchain := NDK_TOOLCHAIN.$(_name)
|
||||
|
||||
# check that the toolchain name is unique
|
||||
$(if $(strip $($(_toolchain).defined)),\
|
||||
$(call __ndk_error,Toolchain $(_name) defined in $(_parent) is\
|
||||
already defined in $(NDK_TOOLCHAIN.$(_name).defined)))
|
||||
|
||||
$(_toolchain).defined := $(_toolchain_config)
|
||||
$(_toolchain).arch := $(_arch)
|
||||
$(_toolchain).abis := $(_abis)
|
||||
$(_toolchain).setup := $(wildcard $(_dir)/setup.mk)
|
||||
|
||||
$(if $(strip $($(_toolchain).setup)),,\
|
||||
$(call __ndk_error, Toolchain $(_name) lacks a setup.mk in $(_dir)))
|
||||
|
||||
NDK_ALL_TOOLCHAINS += $(_name)
|
||||
NDK_ALL_ARCHS += $(_arch)
|
||||
NDK_ALL_ABIS += $(_abis)
|
||||
|
||||
# NDK_ABI.<abi>.toolchains records the list of toolchains that support
|
||||
# a given ABI
|
||||
#
|
||||
$(foreach _abi,$(_abis),\
|
||||
$(eval NDK_ABI.$(_abi).toolchains += $(_name)) \
|
||||
$(eval NDK_ABI.$(_abi).arch := $(sort $(NDK_ABI.$(_abi).arch) $(_arch)))\
|
||||
)
|
||||
|
||||
NDK_ARCH.$(_arch).toolchains += $(_name)
|
||||
NDK_ARCH.$(_arch).abis := $(sort $(NDK_ARCH.$(_arch).abis) $(_abis))
|
||||
|
||||
endif
|
||||
|
||||
# done
|
||||
125
Android/android-ndk-r27d/build/core/build-all.mk
Normal file
@ -0,0 +1,125 @@
|
||||
# Copyright (C) 2009-2010 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
#
|
||||
# This script is used to build all wanted NDK binaries. It is included
|
||||
# by several scripts.
|
||||
#
|
||||
|
||||
# ensure that the following variables are properly defined
|
||||
$(call assert-defined,NDK_APPS NDK_APP_OUT)
|
||||
|
||||
# ====================================================================
|
||||
#
|
||||
# Prepare the build for parsing Android.mk files
|
||||
#
|
||||
# ====================================================================
|
||||
|
||||
# These phony targets are used to control various stages of the build
|
||||
.PHONY: \
|
||||
all \
|
||||
host_libraries \
|
||||
host_executables \
|
||||
installed_modules \
|
||||
executables libraries \
|
||||
static_libraries \
|
||||
shared_libraries \
|
||||
clean clean-objs-dir \
|
||||
clean-executables clean-libraries \
|
||||
clean-installed-modules \
|
||||
clean-installed-binaries \
|
||||
clang_tidy_rules \
|
||||
|
||||
# These macros are used in Android.mk to include the corresponding
|
||||
# build script that will parse the LOCAL_XXX variable definitions.
|
||||
#
|
||||
CLEAR_VARS := $(BUILD_SYSTEM)/clear-vars.mk
|
||||
BUILD_HOST_EXECUTABLE := $(BUILD_SYSTEM)/build-host-executable.mk
|
||||
BUILD_HOST_STATIC_LIBRARY := $(BUILD_SYSTEM)/build-host-static-library.mk
|
||||
BUILD_STATIC_LIBRARY := $(BUILD_SYSTEM)/build-static-library.mk
|
||||
BUILD_SHARED_LIBRARY := $(BUILD_SYSTEM)/build-shared-library.mk
|
||||
BUILD_EXECUTABLE := $(BUILD_SYSTEM)/build-executable.mk
|
||||
PREBUILT_SHARED_LIBRARY := $(BUILD_SYSTEM)/prebuilt-shared-library.mk
|
||||
PREBUILT_STATIC_LIBRARY := $(BUILD_SYSTEM)/prebuilt-static-library.mk
|
||||
|
||||
# this is the list of directories containing dependency information
|
||||
# generated during the build. It will be updated by build scripts
|
||||
# when module definitions are parsed.
|
||||
#
|
||||
ALL_DEPENDENCY_DIRS :=
|
||||
|
||||
# this is the list of all generated files that we would need to clean
|
||||
ALL_HOST_EXECUTABLES :=
|
||||
ALL_HOST_STATIC_LIBRARIES :=
|
||||
ALL_STATIC_LIBRARIES :=
|
||||
ALL_SHARED_LIBRARIES :=
|
||||
ALL_EXECUTABLES :=
|
||||
|
||||
WANTED_INSTALLED_MODULES :=
|
||||
|
||||
# the first rule
|
||||
all: installed_modules host_libraries host_executables clang_tidy_rules
|
||||
|
||||
|
||||
$(foreach _app,$(NDK_APPS),\
|
||||
$(eval include $(BUILD_SYSTEM)/setup-app.mk)\
|
||||
)
|
||||
|
||||
ifeq (,$(strip $(WANTED_INSTALLED_MODULES)))
|
||||
ifneq (,$(strip $(NDK_APP_MODULES)))
|
||||
$(call __ndk_warning,WARNING: No modules to build, your APP_MODULES definition is probably incorrect!)
|
||||
else
|
||||
$(call __ndk_warning,WARNING: There are no modules to build in this project!)
|
||||
endif
|
||||
endif
|
||||
|
||||
# ====================================================================
|
||||
#
|
||||
# Now finish the build preparation with a few rules that depend on
|
||||
# what has been effectively parsed and recorded previously
|
||||
#
|
||||
# ====================================================================
|
||||
|
||||
clean: clean-intermediates clean-installed-binaries
|
||||
|
||||
distclean: clean
|
||||
|
||||
installed_modules: clean-installed-binaries libraries $(WANTED_INSTALLED_MODULES)
|
||||
host_libraries: $(HOST_STATIC_LIBRARIES)
|
||||
host_executables: $(HOST_EXECUTABLES)
|
||||
|
||||
# clang-tidy rules add themselves as dependencies of this phony rule in
|
||||
# ev-clang-tidy.
|
||||
clang_tidy_rules:
|
||||
|
||||
static_libraries: $(STATIC_LIBRARIES)
|
||||
shared_libraries: $(SHARED_LIBRARIES)
|
||||
executables: $(EXECUTABLES)
|
||||
|
||||
ifeq ($(GEN_COMPILE_COMMANDS_DB),true)
|
||||
all: $(COMPILE_COMMANDS_JSON)
|
||||
endif
|
||||
|
||||
libraries: static_libraries shared_libraries
|
||||
|
||||
clean-host-intermediates:
|
||||
$(hide) $(call host-rm,$(HOST_EXECUTABLES) $(HOST_STATIC_LIBRARIES))
|
||||
|
||||
clean-intermediates: clean-host-intermediates
|
||||
$(hide) $(call host-rm,$(EXECUTABLES) $(STATIC_LIBRARIES) $(SHARED_LIBRARIES))
|
||||
|
||||
# include dependency information
|
||||
ALL_DEPENDENCY_DIRS := $(patsubst %/,%,$(sort $(ALL_DEPENDENCY_DIRS)))
|
||||
-include $(wildcard $(ALL_DEPENDENCY_DIRS:%=%/*.d))
|
||||
714
Android/android-ndk-r27d/build/core/build-binary.mk
Normal file
@ -0,0 +1,714 @@
|
||||
# Copyright (C) 2008 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# Check that LOCAL_MODULE is defined, then restore its LOCAL_XXXX values
|
||||
$(call assert-defined,LOCAL_MODULE)
|
||||
$(call module-restore-locals,$(LOCAL_MODULE))
|
||||
|
||||
# As in build-module.mk, eval sucks. Manually unstash the flags variations to
|
||||
# preserve -Werror=#warnings.
|
||||
LOCAL_ASFLAGS := $(__ndk_modules.$(LOCAL_MODULE).ASFLAGS)
|
||||
LOCAL_ASMFLAGS := $(__ndk_modules.$(LOCAL_MODULE).ASMFLAGS)
|
||||
LOCAL_CFLAGS := $(__ndk_modules.$(LOCAL_MODULE).CFLAGS)
|
||||
LOCAL_CLANG_TIDY_FLAGS := $(__ndk_modules.$(LOCAL_MODULE).CLANG_TIDY_FLAGS)
|
||||
LOCAL_CONLYFLAGS := $(__ndk_modules.$(LOCAL_MODULE).CONLYFLAGS)
|
||||
LOCAL_CPPFLAGS := $(__ndk_modules.$(LOCAL_MODULE).CPPFLAGS)
|
||||
LOCAL_CXXFLAGS := $(__ndk_modules.$(LOCAL_MODULE).CXXFLAGS)
|
||||
LOCAL_LDFLAGS := $(__ndk_modules.$(LOCAL_MODULE).LDFLAGS)
|
||||
|
||||
# For now, only support target (device-specific modules).
|
||||
# We may want to introduce support for host modules in the future
|
||||
# but that is too experimental for now.
|
||||
#
|
||||
my := POISONED
|
||||
|
||||
# LOCAL_MAKEFILE must also exist and name the Android.mk that
|
||||
# included the module build script.
|
||||
#
|
||||
$(call assert-defined,LOCAL_MAKEFILE LOCAL_BUILD_SCRIPT LOCAL_BUILT_MODULE)
|
||||
|
||||
# A list of LOCAL_XXX variables that are ignored for static libraries.
|
||||
# Print a warning if they are present inside a module definition to let
|
||||
# the user know this won't do what he/she expects.
|
||||
not_in_static_libs := \
|
||||
LOCAL_LDFLAGS \
|
||||
LOCAL_LDLIBS \
|
||||
LOCAL_ALLOW_UNDEFINED_SYMBOLS
|
||||
|
||||
ifeq ($(call module-get-class,$(LOCAL_MODULE)),STATIC_LIBRARY)
|
||||
$(foreach _notvar,$(not_in_static_libs),\
|
||||
$(if $(strip $($(_notvar))),\
|
||||
$(call __ndk_info,WARNING:$(LOCAL_MAKEFILE):$(LOCAL_MODULE): $(_notvar) is always ignored for static libraries)\
|
||||
)\
|
||||
)
|
||||
endif
|
||||
|
||||
# Some developers like to add library names (e.g. -lfoo) to LOCAL_LDLIBS
|
||||
# and LOCAL_LDFLAGS directly. This is very fragile and can lead to broken
|
||||
# builds and other nasty surprises, because it doesn't tell ndk-build
|
||||
# that the corresponding module depends on these files. Emit a warning
|
||||
# when we detect this case.
|
||||
libs_in_ldflags := $(filter -l% %.so %.a,$(LOCAL_LDLIBS) $(LOCAL_LDFLAGS))
|
||||
|
||||
# Since the above will glob anything ending in .so or .a, we need to filter out
|
||||
# any cases of -Wl,--exclude-libs since we use that to hide symbols in STLs.
|
||||
libs_in_ldflags := \
|
||||
$(filter-out -Wl$(comma)--exclude-libs$(comma)%,$(libs_in_ldflags))
|
||||
|
||||
include $(BUILD_SYSTEM)/system_libs.mk
|
||||
|
||||
# The only way to statically link libomp.a is with
|
||||
# `-Wl,-Bstatic -lomp -Wl,-Bdynamic`, so we need to accept `-lomp`.
|
||||
# https://github.com/android-ndk/ndk/issues/1028
|
||||
NDK_SYSTEM_LIBS += libomp.so
|
||||
|
||||
libs_in_ldflags := $(filter-out $(NDK_SYSTEM_LIBS:lib%.so=-l%),$(libs_in_ldflags))
|
||||
|
||||
ifneq (,$(strip $(libs_in_ldflags)))
|
||||
$(call __ndk_info,WARNING:$(LOCAL_MAKEFILE):$(LOCAL_MODULE): non-system libraries in linker flags: $(libs_in_ldflags))
|
||||
$(call __ndk_info, This is likely to result in incorrect builds. Try using LOCAL_STATIC_LIBRARIES)
|
||||
$(call __ndk_info, or LOCAL_SHARED_LIBRARIES instead to list the library dependencies of the)
|
||||
$(call __ndk_info, current module)
|
||||
endif
|
||||
|
||||
include $(BUILD_SYSTEM)/import-locals.mk
|
||||
|
||||
# Check for LOCAL_THIN_ARCHIVE / APP_THIN_ARCHIVE and print a warning if
|
||||
# it is defined for non-static library modules.
|
||||
thin_archive := $(strip $(LOCAL_THIN_ARCHIVE))
|
||||
ifdef thin_archive
|
||||
ifneq (STATIC_LIBRARY,$(call module-get-class,$(LOCAL_MODULE)))
|
||||
$(call __ndk_info,WARNING:$(LOCAL_MAKEFILE):$(LOCAL_MODULE): LOCAL_THIN_ARCHIVE is for building static libraries)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef thin_archive
|
||||
thin_archive := $(strip $(NDK_APP_THIN_ARCHIVE))
|
||||
endif
|
||||
# Print a warning if the value is not 'true', 'false' or empty.
|
||||
ifneq (,$(filter-out true false,$(thin_archive)))
|
||||
$(call __ndk_info,WARNING:$(LOCAL_MAKEFILE):$(LOCAL_MODULE): Invalid LOCAL_THIN_ARCHIVE value '$(thin_archive)' ignored!)
|
||||
thin_archive :=
|
||||
endif
|
||||
|
||||
#
|
||||
# Ensure that 'make <module>' and 'make clean-<module>' work
|
||||
#
|
||||
.PHONY: $(LOCAL_MODULE)
|
||||
$(LOCAL_MODULE): $(LOCAL_BUILT_MODULE)
|
||||
|
||||
cleantarget := clean-$(LOCAL_MODULE)-$(TARGET_ARCH_ABI)
|
||||
.PHONY: $(cleantarget)
|
||||
clean: $(cleantarget)
|
||||
|
||||
$(cleantarget): PRIVATE_ABI := $(TARGET_ARCH_ABI)
|
||||
$(cleantarget): PRIVATE_MODULE := $(LOCAL_MODULE)
|
||||
ifneq ($(LOCAL_BUILT_MODULE_NOT_COPIED),true)
|
||||
$(cleantarget): PRIVATE_CLEAN_FILES := $(LOCAL_BUILT_MODULE) \
|
||||
$(LOCAL_OBJS_DIR)
|
||||
else
|
||||
$(cleantarget): PRIVATE_CLEAN_FILES := $(LOCAL_OBJS_DIR)
|
||||
endif
|
||||
$(cleantarget)::
|
||||
$(call host-echo-build-step,$(PRIVATE_ABI),Clean) "$(PRIVATE_MODULE) [$(PRIVATE_ABI)]"
|
||||
$(hide) $(call host-rmdir,$(PRIVATE_CLEAN_FILES))
|
||||
|
||||
# list of generated object files
|
||||
LOCAL_OBJECTS :=
|
||||
|
||||
# always define ANDROID when building binaries
|
||||
#
|
||||
LOCAL_CFLAGS := -DANDROID $(LOCAL_CFLAGS)
|
||||
|
||||
ifeq ($(APP_SUPPORT_FLEXIBLE_PAGE_SIZES),true)
|
||||
LOCAL_CFLAGS += -D__BIONIC_NO_PAGE_SIZE_MACRO
|
||||
ifneq (,$(filter $(APP_ABI),arm64-v8a x86_64))
|
||||
LOCAL_LDFLAGS += -Wl,-z,max-page-size=16384
|
||||
endif
|
||||
endif
|
||||
|
||||
#
|
||||
# Add the default system shared libraries to the build
|
||||
#
|
||||
ifeq ($(LOCAL_SYSTEM_SHARED_LIBRARIES),none)
|
||||
LOCAL_SHARED_LIBRARIES += $(TARGET_DEFAULT_SYSTEM_SHARED_LIBRARIES)
|
||||
else
|
||||
LOCAL_SHARED_LIBRARIES += $(LOCAL_SYSTEM_SHARED_LIBRARIES)
|
||||
endif
|
||||
|
||||
#
|
||||
# Check LOCAL_CPP_EXTENSION
|
||||
#
|
||||
bad_cpp_extensions := $(strip $(filter-out .%,$(LOCAL_CPP_EXTENSION)))
|
||||
ifdef bad_cpp_extensions
|
||||
$(call __ndk_info,WARNING: Invalid LOCAL_CPP_EXTENSION values: $(bad_cpp_extensions))
|
||||
LOCAL_CPP_EXTENSION := $(filter $(bad_cpp_extensions),$(LOCAL_CPP_EXTENSIONS))
|
||||
endif
|
||||
LOCAL_CPP_EXTENSION := $(strip $(LOCAL_CPP_EXTENSION))
|
||||
ifeq ($(LOCAL_CPP_EXTENSION),)
|
||||
# Match the default GCC C++ extensions.
|
||||
LOCAL_CPP_EXTENSION := $(default-c++-extensions)
|
||||
endif
|
||||
|
||||
include $(BUILD_SYSTEM)/stl.mk
|
||||
|
||||
#
|
||||
# If LOCAL_ALLOW_UNDEFINED_SYMBOLS is not true, the linker will allow the generation
|
||||
# of a binary that uses undefined symbols.
|
||||
#
|
||||
ifneq ($(LOCAL_ALLOW_UNDEFINED_SYMBOLS),true)
|
||||
LOCAL_LDFLAGS += $(TARGET_NO_UNDEFINED_LDFLAGS)
|
||||
endif
|
||||
|
||||
# We enable fatal linker warnings by default.
|
||||
# If LOCAL_DISABLE_FATAL_LINKER_WARNINGS is true, we don't enable this check.
|
||||
ifneq ($(LOCAL_DISABLE_FATAL_LINKER_WARNINGS),true)
|
||||
LOCAL_LDFLAGS += -Wl,--fatal-warnings
|
||||
endif
|
||||
|
||||
# By default, we protect against format string vulnerabilities
|
||||
# If LOCAL_DISABLE_FORMAT_STRING_CHECKS is true, we disable the protections.
|
||||
ifeq ($(LOCAL_DISABLE_FORMAT_STRING_CHECKS),true)
|
||||
LOCAL_CFLAGS += $(TARGET_DISABLE_FORMAT_STRING_CFLAGS)
|
||||
else
|
||||
LOCAL_CFLAGS += $(TARGET_FORMAT_STRING_CFLAGS)
|
||||
endif
|
||||
|
||||
# Enable branch protection for arm64-v8a
|
||||
LOCAL_BRANCH_PROTECTION := $(strip $(LOCAL_BRANCH_PROTECTION))
|
||||
ifdef LOCAL_BRANCH_PROTECTION
|
||||
ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
|
||||
LOCAL_CFLAGS += -mbranch-protection=$(LOCAL_BRANCH_PROTECTION)
|
||||
endif
|
||||
endif
|
||||
|
||||
# http://b.android.com/222239
|
||||
# http://b.android.com/220159 (internal http://b/31809417)
|
||||
# x86 devices have stack alignment issues.
|
||||
ifeq ($(TARGET_ARCH_ABI),x86)
|
||||
ifneq (,$(call lt,$(APP_PLATFORM_LEVEL),24))
|
||||
LOCAL_CFLAGS += -mstackrealign
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(LOCAL_ALLOW_UNDEFINED_VERSION_SCRIPT_SYMBOLS),true)
|
||||
LOCAL_LDFLAGS += -Wl,--no-undefined-version
|
||||
endif
|
||||
|
||||
#
|
||||
# The original Android build system allows you to use the .arm prefix
|
||||
# to a source file name to indicate that it should be defined in either
|
||||
# 'thumb' or 'arm' mode, depending on the value of LOCAL_ARM_MODE
|
||||
#
|
||||
# First, check LOCAL_ARM_MODE, it should be empty, 'thumb' or 'arm'
|
||||
# We make the default 'thumb'
|
||||
#
|
||||
LOCAL_ARM_MODE := $(strip $(LOCAL_ARM_MODE))
|
||||
ifdef LOCAL_ARM_MODE
|
||||
ifneq ($(words $(LOCAL_ARM_MODE)),1)
|
||||
$(call __ndk_info, LOCAL_ARM_MODE in $(LOCAL_MAKEFILE) must be one word, not '$(LOCAL_ARM_MODE)')
|
||||
$(call __ndk_error, Aborting)
|
||||
endif
|
||||
# check that LOCAL_ARM_MODE is defined to either 'arm' or 'thumb'
|
||||
$(if $(filter-out thumb arm, $(LOCAL_ARM_MODE)),\
|
||||
$(call __ndk_info, LOCAL_ARM_MODE must be defined to either 'arm' or 'thumb' in $(LOCAL_MAKEFILE) not '$(LOCAL_ARM_MODE)')\
|
||||
$(call __ndk_error, Aborting)\
|
||||
)
|
||||
my_link_arm_mode := $(LOCAL_ARM_MODE)
|
||||
else
|
||||
my_link_arm_mode := thumb
|
||||
endif
|
||||
|
||||
# As a special case, the original Android build system
|
||||
# allows one to specify that certain source files can be
|
||||
# forced to build in ARM mode by using a '.arm' suffix
|
||||
# after the extension, e.g.
|
||||
#
|
||||
# LOCAL_SRC_FILES := foo.c.arm
|
||||
#
|
||||
# to build source file $(LOCAL_PATH)/foo.c as ARM
|
||||
#
|
||||
|
||||
$(call clear-all-src-tags)
|
||||
|
||||
# Historically the NDK supported both Neon and non-Neon as variants of the
|
||||
# armeabi-v7a ABI. These were practically two ABIs but the distinction was not
|
||||
# official (APKs did not have separate libraries for Neon and non-Neon devices).
|
||||
# As of NDK r24 non-Neon devices are no longer supported, so any options opting
|
||||
# *in* to Neon are ignored, and options explicitly opting out of Neon are an
|
||||
# error. Users that choose a non-Neon -mfpu in their CFLAGS will receive no
|
||||
# diagnostic.
|
||||
|
||||
LOCAL_ARM_NEON := $(strip $(LOCAL_ARM_NEON))
|
||||
|
||||
ifeq ($(LOCAL_ARM_NEON),false)
|
||||
$(call __ndk_error,Building non-Neon code is no longer supported.)
|
||||
endif
|
||||
|
||||
LOCAL_SRC_FILES := $(LOCAL_SRC_FILES:%.neon=%)
|
||||
|
||||
# strip the .arm suffix from LOCAL_SRC_FILES
|
||||
# and tag the relevant sources with the 'arm' tag
|
||||
#
|
||||
arm_sources := $(filter %.arm,$(LOCAL_SRC_FILES))
|
||||
arm_sources := $(arm_sources:%.arm=%)
|
||||
thumb_sources := $(filter-out %.arm,$(LOCAL_SRC_FILES))
|
||||
LOCAL_SRC_FILES := $(LOCAL_SRC_FILES:%.arm=%)
|
||||
|
||||
ifeq ($(LOCAL_ARM_MODE),arm)
|
||||
arm_sources := $(LOCAL_SRC_FILES)
|
||||
# tag the precompiled header with 'arm' tag if it exists
|
||||
ifneq (,$(LOCAL_PCH))
|
||||
$(call tag-src-files,$(LOCAL_PCH),arm)
|
||||
endif
|
||||
endif
|
||||
ifeq ($(LOCAL_ARM_MODE),thumb)
|
||||
arm_sources := $(empty)
|
||||
endif
|
||||
$(call tag-src-files,$(arm_sources),arm)
|
||||
|
||||
# tag debug if APP_OPTIM is 'debug'
|
||||
#
|
||||
ifeq ($(APP_OPTIM),debug)
|
||||
$(call tag-src-files,$(LOCAL_SRC_FILES),debug)
|
||||
ifneq (,$(LOCAL_PCH))
|
||||
$(call tag-src-files,$(LOCAL_PCH),debug)
|
||||
endif
|
||||
endif
|
||||
|
||||
# add PCH to LOCAL_SRC_FILES so that TARGET-process-src-files-tags could process it
|
||||
ifneq (,$(LOCAL_PCH))
|
||||
LOCAL_SRC_FILES += $(LOCAL_PCH)
|
||||
endif
|
||||
|
||||
# Process all source file tags to determine toolchain-specific
|
||||
# target compiler flags, and text.
|
||||
#
|
||||
$(call TARGET-process-src-files-tags)
|
||||
|
||||
# now remove PCH from LOCAL_SRC_FILES to prevent getting NDK warning about
|
||||
# unsupported source file extensions
|
||||
ifneq (,$(LOCAL_PCH))
|
||||
LOCAL_SRC_FILES := $(filter-out $(LOCAL_PCH),$(LOCAL_SRC_FILES))
|
||||
endif
|
||||
|
||||
# only call dump-src-file-tags during debugging
|
||||
#$(dump-src-file-tags)
|
||||
|
||||
LOCAL_DEPENDENCY_DIRS :=
|
||||
|
||||
# all_source_patterns contains the list of filename patterns that correspond
|
||||
# to source files recognized by our build system
|
||||
ifneq ($(filter x86 x86_64, $(TARGET_ARCH_ABI)),)
|
||||
all_source_extensions := .c .s .S .asm $(LOCAL_CPP_EXTENSION)
|
||||
else
|
||||
all_source_extensions := .c .s .S $(LOCAL_CPP_EXTENSION)
|
||||
endif
|
||||
all_source_patterns := $(foreach _ext,$(all_source_extensions),%$(_ext))
|
||||
all_cpp_patterns := $(foreach _ext,$(LOCAL_CPP_EXTENSION),%$(_ext))
|
||||
|
||||
unknown_sources := $(strip $(filter-out $(all_source_patterns),$(LOCAL_SRC_FILES)))
|
||||
ifdef unknown_sources
|
||||
$(call __ndk_info,WARNING: Unsupported source file extensions in $(LOCAL_MAKEFILE) for module $(LOCAL_MODULE))
|
||||
$(call __ndk_info, $(unknown_sources))
|
||||
endif
|
||||
|
||||
# LOCAL_OBJECTS will list all object files corresponding to the sources
|
||||
# listed in LOCAL_SRC_FILES, in the *same* order.
|
||||
#
|
||||
LOCAL_OBJECTS := $(LOCAL_SRC_FILES)
|
||||
$(foreach _ext,$(all_source_extensions),\
|
||||
$(eval LOCAL_OBJECTS := $$(LOCAL_OBJECTS:%$(_ext)=%$$(TARGET_OBJ_EXTENSION)))\
|
||||
)
|
||||
LOCAL_OBJECTS := $(filter %$(TARGET_OBJ_EXTENSION),$(LOCAL_OBJECTS))
|
||||
LOCAL_OBJECTS := $(subst ../,__/,$(LOCAL_OBJECTS))
|
||||
LOCAL_OBJECTS := $(subst :,_,$(LOCAL_OBJECTS))
|
||||
LOCAL_OBJECTS := $(foreach _obj,$(LOCAL_OBJECTS),$(LOCAL_OBJS_DIR)/$(_obj))
|
||||
|
||||
# If the module has any kind of C++ features, enable them in LOCAL_CPPFLAGS
|
||||
#
|
||||
ifneq (,$(call module-has-c++-features,$(LOCAL_MODULE),rtti))
|
||||
LOCAL_CPPFLAGS += -frtti
|
||||
endif
|
||||
ifneq (,$(call module-has-c++-features,$(LOCAL_MODULE),exceptions))
|
||||
LOCAL_CPPFLAGS += -fexceptions
|
||||
endif
|
||||
|
||||
# Build PCH
|
||||
|
||||
get-pch-name = $(strip \
|
||||
$(subst ../,__/,\
|
||||
$(eval __pch := $1)\
|
||||
$(eval __pch := $(__pch:%.h=%.precompiled.h))\
|
||||
$(__pch)\
|
||||
))
|
||||
|
||||
ifneq (,$(LOCAL_PCH))
|
||||
# Build PCH into obj directory
|
||||
LOCAL_BUILT_PCH := $(call get-pch-name,$(LOCAL_PCH))
|
||||
|
||||
# Clang whines about a "c-header" (.h rather than .hpp) being used in C++
|
||||
# mode (note that we use compile-cpp-source to build the header).
|
||||
LOCAL_SRC_FILES_TARGET_CFLAGS.$(LOCAL_PCH) += -x c++-header
|
||||
|
||||
# Build PCH
|
||||
$(call compile-cpp-source,$(LOCAL_PCH),$(LOCAL_BUILT_PCH).gch)
|
||||
|
||||
# The PCH must be compiled the same way as the sources (thumb vs arm must
|
||||
# match). This means that we'd have to generate a PCH for both foo.c and
|
||||
# foo.c.arm.
|
||||
#
|
||||
# Since files with those source tags should be the minority, precompiling
|
||||
# that header might be a net loss compared to just using it normally. As
|
||||
# such, we only use the PCH for the default compilation mode for the module.
|
||||
#
|
||||
# See https://github.com/android-ndk/ndk/issues/14
|
||||
TAGS_TO_FILTER :=
|
||||
|
||||
# If we're building thumb, strip out .arm files.
|
||||
ifneq (arm,$(LOCAL_ARM_MODE))
|
||||
TAGS_TO_FILTER += arm
|
||||
endif
|
||||
|
||||
# There is no .thumb. No need to filter them out if we're building ARM.
|
||||
|
||||
allowed_src := $(foreach src,$(filter $(all_cpp_patterns),$(LOCAL_SRC_FILES)),\
|
||||
$(if $(filter $(TAGS_TO_FILTER),$(LOCAL_SRC_FILES_TAGS.$(src))),,$(src))\
|
||||
)
|
||||
# All files without tags depend on PCH
|
||||
$(foreach src,$(allowed_src),\
|
||||
$(eval $(LOCAL_OBJS_DIR)/$(call get-object-name,$(src)) : $(LOCAL_OBJS_DIR)/$(LOCAL_BUILT_PCH).gch)\
|
||||
)
|
||||
# Make sure those files are built with PCH
|
||||
$(call add-src-files-target-cflags,$(allowed_src),-Winvalid-pch -include $(LOCAL_OBJS_DIR)/$(LOCAL_BUILT_PCH))
|
||||
|
||||
# Insert PCH dir at beginning of include search path
|
||||
LOCAL_C_INCLUDES := \
|
||||
$(LOCAL_OBJS_DIR) \
|
||||
$(LOCAL_C_INCLUDES)
|
||||
endif
|
||||
|
||||
# Build the sources to object files
|
||||
#
|
||||
|
||||
do_tidy := $(NDK_APP_CLANG_TIDY)
|
||||
ifdef LOCAL_CLANG_TIDY
|
||||
do_tidy := $(LOCAL_CLANG_TIDY)
|
||||
endif
|
||||
|
||||
ifeq ($(do_tidy),true)
|
||||
$(foreach src,$(filter %.c,$(LOCAL_SRC_FILES)),\
|
||||
$(call clang-tidy-c,$(src),$(call get-object-name,$(src))))
|
||||
$(foreach src,$(filter $(all_cpp_patterns),$(LOCAL_SRC_FILES)),\
|
||||
$(call clang-tidy-cpp,$(src),$(call get-object-name,$(src))))
|
||||
endif
|
||||
|
||||
$(foreach src,$(filter %.c,$(LOCAL_SRC_FILES)), $(call compile-c-source,$(src),$(call get-object-name,$(src))))
|
||||
$(foreach src,$(filter %.S %.s,$(LOCAL_SRC_FILES)), $(call compile-s-source,$(src),$(call get-object-name,$(src))))
|
||||
$(foreach src,$(filter $(all_cpp_patterns),$(LOCAL_SRC_FILES)),\
|
||||
$(call compile-cpp-source,$(src),$(call get-object-name,$(src)))\
|
||||
)
|
||||
|
||||
ifneq ($(filter x86 x86_64, $(TARGET_ARCH_ABI)),)
|
||||
$(foreach src,$(filter %.asm,$(LOCAL_SRC_FILES)), $(call compile-asm-source,$(src),$(call get-object-name,$(src))))
|
||||
endif
|
||||
|
||||
#
|
||||
# The compile-xxx-source calls updated LOCAL_OBJECTS and LOCAL_DEPENDENCY_DIRS
|
||||
#
|
||||
ALL_DEPENDENCY_DIRS += $(sort $(LOCAL_DEPENDENCY_DIRS))
|
||||
CLEAN_OBJS_DIRS += $(LOCAL_OBJS_DIR)
|
||||
|
||||
#
|
||||
# Handle the static and shared libraries this module depends on
|
||||
#
|
||||
|
||||
# https://github.com/android/ndk/issues/885
|
||||
# If we're using LLD we need to use a slower build-id algorithm to work around
|
||||
# the old version of LLDB in Android Studio, which doesn't understand LLD's
|
||||
# default hash ("fast").
|
||||
linker_ldflags := -Wl,--build-id=sha1
|
||||
|
||||
ifneq (,$(call lt,$(APP_PLATFORM_LEVEL),30))
|
||||
# https://github.com/android/ndk/issues/1196
|
||||
# https://github.com/android/ndk/issues/1589
|
||||
linker_ldflags += -Wl,--no-rosegment
|
||||
endif
|
||||
|
||||
my_ldflags := $(TARGET_LDFLAGS) $(linker_ldflags) $(NDK_APP_LDFLAGS) $(LOCAL_LDFLAGS)
|
||||
|
||||
# https://github.com/android/ndk/issues/1390
|
||||
# Only a warning rather than an error because the API level cannot be configured
|
||||
# on a per-module basis. If the user has an APP_PLATFORM that happens to be able
|
||||
# to build the static executables there's no need to fail the build.
|
||||
ifneq (,$(filter -static,$(my_ldflags)))
|
||||
ifneq ($(APP_PLATFORM),$(NDK_MAX_PLATFORM))
|
||||
$(call __ndk_info,WARNING: Building static executable but APP_PLATFORM \
|
||||
$(APP_PLATFORM) is not the latest API level $(NDK_MAX_PLATFORM). \
|
||||
Build may not succeed.)
|
||||
endif
|
||||
endif
|
||||
|
||||
# When LOCAL_SHORT_COMMANDS is defined to 'true' we are going to write the
|
||||
# list of all object files and/or static/shared libraries that appear on the
|
||||
# command line to a file, then use the @<listfile> syntax to invoke it.
|
||||
#
|
||||
# This allows us to link or archive a huge number of stuff even on Windows
|
||||
# with its puny 8192 max character limit on its command-line.
|
||||
#
|
||||
LOCAL_SHORT_COMMANDS := $(strip $(LOCAL_SHORT_COMMANDS))
|
||||
ifndef LOCAL_SHORT_COMMANDS
|
||||
LOCAL_SHORT_COMMANDS := $(strip $(NDK_APP_SHORT_COMMANDS))
|
||||
endif
|
||||
|
||||
$(call generate-file-dir,$(LOCAL_BUILT_MODULE))
|
||||
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_OBJECTS := $(LOCAL_OBJECTS)
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_LIBATOMIC := $(TARGET_LIBATOMIC)
|
||||
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_LD := $(TARGET_LD)
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_LDFLAGS := $(my_ldflags)
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_LDLIBS := $(LOCAL_LDLIBS) $(TARGET_LDLIBS)
|
||||
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_NAME := $(notdir $(LOCAL_BUILT_MODULE))
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_CXX := $(TARGET_CXX)
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_CC := $(TARGET_CC)
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_SYSROOT_API_LIB_DIR := $(SYSROOT_API_LIB_DIR)
|
||||
|
||||
ifeq (,$(call module_needs_clangxx,$(LOCAL_MODULE)))
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_LD_DRIVER := $(TARGET_CC)
|
||||
else
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_LD_DRIVER := $(TARGET_CXX)
|
||||
endif
|
||||
|
||||
ifeq ($(call module-get-class,$(LOCAL_MODULE)),STATIC_LIBRARY)
|
||||
|
||||
#
|
||||
# This is a static library module, things are very easy. We only need
|
||||
# to build the object files and archive them with 'ar'. Note that module
|
||||
# dependencies can be ignored here, i.e. if the module depends on other
|
||||
# static or shared libraries, there is no need to actually build them
|
||||
# before, so don't add Make dependencies to them.
|
||||
#
|
||||
# In other words, consider the following graph:
|
||||
#
|
||||
# libfoo.so -> libA.a ->libB.a
|
||||
#
|
||||
# then libA.a and libB.a can be built in parallel, only linking libfoo.so
|
||||
# depends on their completion.
|
||||
#
|
||||
|
||||
ar_objects := $(call host-path,$(LOCAL_OBJECTS))
|
||||
|
||||
ifeq ($(LOCAL_SHORT_COMMANDS),true)
|
||||
$(call ndk_log,Building static library module '$(LOCAL_MODULE)' with linker list file)
|
||||
ar_list_file := $(LOCAL_OBJS_DIR)/archiver.list
|
||||
$(call generate-list-file,\
|
||||
$(call escape-backslashes,$(ar_objects)),$(ar_list_file))
|
||||
ar_objects := @$(call host-path,$(ar_list_file))
|
||||
$(LOCAL_BUILT_MODULE): $(ar_list_file)
|
||||
endif
|
||||
|
||||
# Compute 'ar' flags. Thin archives simply require 'T' here.
|
||||
ar_flags := $(TARGET_ARFLAGS)
|
||||
ifeq (true,$(thin_archive))
|
||||
$(call ndk_log,$(TARGET_ARCH_ABI):Building static library '$(LOCAL_MODULE)' as thin archive)
|
||||
ar_flags := $(ar_flags)T
|
||||
endif
|
||||
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_ABI := $(TARGET_ARCH_ABI)
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_AR := $(TARGET_AR) $(ar_flags) $(TARGET_AR_FLAGS)
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_AR_OBJECTS := $(ar_objects)
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_BUILD_STATIC_LIB := $(cmd-build-static-library)
|
||||
|
||||
$(LOCAL_BUILT_MODULE): $(LOCAL_OBJECTS)
|
||||
$(call host-echo-build-step,$(PRIVATE_ABI),StaticLibrary) "$(PRIVATE_NAME)"
|
||||
$(hide) $(call host-rm,$@)
|
||||
$(hide) $(PRIVATE_BUILD_STATIC_LIB)
|
||||
|
||||
ALL_STATIC_LIBRARIES += $(LOCAL_BUILT_MODULE)
|
||||
|
||||
endif
|
||||
|
||||
ifneq (,$(filter SHARED_LIBRARY EXECUTABLE,$(call module-get-class,$(LOCAL_MODULE))))
|
||||
|
||||
#
|
||||
# This is a shared library or an executable, so computing dependencies properly is
|
||||
# crucial. The general rule to apply is the following:
|
||||
#
|
||||
# - collect the list of all static libraries that need to be part
|
||||
# of the link, and in the right order. To do so, get the transitive
|
||||
# closure of LOCAL_STATIC_LIBRARIES and LOCAL_WHOLE_STATIC_LIBRARIES
|
||||
# and ensure they are ordered topologically.
|
||||
#
|
||||
# - collect the list of all shared libraries that need to be part of
|
||||
# the link. This is the transitive closure of the list of
|
||||
# LOCAL_SHARED_LIBRARIES for the module and all its dependent static
|
||||
# libraries identified in the step above. Of course, need to be
|
||||
# ordered topologically too.
|
||||
#
|
||||
# - add Make dependencies to ensure that all these libs are built
|
||||
# before the module itself too.
|
||||
#
|
||||
# A few quick examples:
|
||||
#
|
||||
# main.exe -> libA.a -> libB.a -> libfoo.so -> libC.a
|
||||
#
|
||||
# static_libs(main.exe) = libA.a libB.a (i.e. no libC.a)
|
||||
# shared_libs(main.exe) = libfoo.so
|
||||
# static_libs(libfoo.so) = libC.a
|
||||
#
|
||||
# main.exe -> libA.a ---> libB.a
|
||||
# | ^
|
||||
# v |
|
||||
# libC.a ------
|
||||
#
|
||||
# static_libs(main.exe) = libA.a libC.a libB.a
|
||||
# (i.e. libB.a must appear after all libraries that depend on it).
|
||||
#
|
||||
all_libs := $(call module-get-link-libs,$(LOCAL_MODULE))
|
||||
shared_libs := $(call module-filter-shared-libraries,$(all_libs))
|
||||
static_libs := $(call module-filter-static-libraries,$(all_libs))
|
||||
whole_static_libs := $(call module-extract-whole-static-libs,$(LOCAL_MODULE),$(static_libs))
|
||||
static_libs := $(filter-out $(whole_static_libs),$(static_libs))
|
||||
all_defined_libs := $(shared_libs) $(static_libs) $(whole_static_libs)
|
||||
undefined_libs := $(filter-out $(all_defined_libs),$(all_libs))
|
||||
|
||||
ifdef undefined_libs
|
||||
$(call __ndk_warning,Module $(LOCAL_MODULE) depends on undefined modules: $(undefined_libs))
|
||||
|
||||
# https://github.com/android-ndk/ndk/issues/208
|
||||
# ndk-build didn't used to fail the build for a missing dependency. This
|
||||
# seems to have always been the behavior, so there's a good chance that
|
||||
# there are builds out there that depend on this behavior (as of right now,
|
||||
# anything using libc++ on ARM has this problem because of libunwind).
|
||||
#
|
||||
# By default we will abort in this situation because this is so completely
|
||||
# broken. A user may define APP_ALLOW_MISSING_DEPS to "true" in their
|
||||
# Application.mk or on the command line to revert to the old, broken
|
||||
# behavior.
|
||||
ifneq ($(APP_ALLOW_MISSING_DEPS),true)
|
||||
$(call __ndk_error,Note that old versions of ndk-build silently ignored \
|
||||
this error case. If your project worked on those versions$(comma) \
|
||||
the missing libraries were not needed and you can remove those \
|
||||
dependencies from the module to fix your build. \
|
||||
Alternatively$(comma) set APP_ALLOW_MISSING_DEPS=true to allow \
|
||||
missing dependencies.)
|
||||
$(call __ndk_error,Aborting.)
|
||||
endif
|
||||
endif
|
||||
|
||||
$(call -ndk-mod-debug,module $(LOCAL_MODULE) [$(LOCAL_BUILT_MODULE)])
|
||||
$(call -ndk-mod-debug,. all_libs='$(all_libs)')
|
||||
$(call -ndk-mod-debug,. shared_libs='$(shared_libs)')
|
||||
$(call -ndk-mod-debug,. static_libs='$(static_libs)')
|
||||
$(call -ndk-mod-debug,. whole_static_libs='$(whole_static_libs)')
|
||||
|
||||
shared_libs := $(call map,module-get-built,$(shared_libs))
|
||||
static_libs := $(call map,module-get-built,$(static_libs))
|
||||
whole_static_libs := $(call map,module-get-built,$(whole_static_libs))
|
||||
|
||||
$(call -ndk-mod-debug,. built_shared_libs='$(shared_libs)')
|
||||
$(call -ndk-mod-debug,. built_static_libs='$(static_libs)')
|
||||
$(call -ndk-mod-debug,. built_whole_static_libs='$(whole_static_libs)')
|
||||
|
||||
# The list of object/static/shared libraries passed to the linker when
|
||||
# building shared libraries and executables. order is important.
|
||||
linker_objects_and_libraries = $(strip $(call TARGET-get-linker-objects-and-libraries,\
|
||||
$(LOCAL_OBJECTS), \
|
||||
$(static_libs), \
|
||||
$(whole_static_libs), \
|
||||
$(shared_libs)))
|
||||
|
||||
ifeq ($(LOCAL_SHORT_COMMANDS),true)
|
||||
$(call ndk_log,Building ELF binary module '$(LOCAL_MODULE)' with linker list file)
|
||||
linker_options := $(linker_objects_and_libraries)
|
||||
linker_list_file := $(LOCAL_OBJS_DIR)/linker.list
|
||||
linker_objects_and_libraries := @$(call host-path,$(linker_list_file))
|
||||
$(call generate-list-file,\
|
||||
$(call escape-backslashes,$(linker_options)),$(linker_list_file))
|
||||
$(LOCAL_BUILT_MODULE): $(linker_list_file)
|
||||
endif
|
||||
|
||||
$(LOCAL_BUILT_MODULE): $(shared_libs) $(static_libs) $(whole_static_libs)
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_ABI := $(TARGET_ARCH_ABI)
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_LINKER_OBJECTS_AND_LIBRARIES := $(linker_objects_and_libraries)
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_STATIC_LIBRARIES := $(static_libs)
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_WHOLE_STATIC_LIBRARIES := $(whole_static_libs)
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_SHARED_LIBRARIES := $(shared_libs)
|
||||
|
||||
endif
|
||||
|
||||
#
|
||||
# If this is a shared library module
|
||||
#
|
||||
ifeq ($(call module-get-class,$(LOCAL_MODULE)),SHARED_LIBRARY)
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_BUILD_SHARED_LIB := $(cmd-build-shared-library)
|
||||
$(LOCAL_BUILT_MODULE): $(LOCAL_OBJECTS)
|
||||
$(call host-echo-build-step,$(PRIVATE_ABI),SharedLibrary) "$(PRIVATE_NAME)"
|
||||
$(hide) $(PRIVATE_BUILD_SHARED_LIB)
|
||||
|
||||
ALL_SHARED_LIBRARIES += $(LOCAL_BUILT_MODULE)
|
||||
endif
|
||||
|
||||
#
|
||||
# If this is an executable module
|
||||
#
|
||||
ifeq ($(call module-get-class,$(LOCAL_MODULE)),EXECUTABLE)
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_ABI := $(TARGET_ARCH_ABI)
|
||||
$(LOCAL_BUILT_MODULE): PRIVATE_BUILD_EXECUTABLE := $(cmd-build-executable)
|
||||
$(LOCAL_BUILT_MODULE): $(LOCAL_OBJECTS)
|
||||
$(call host-echo-build-step,$(PRIVATE_ABI),Executable) "$(PRIVATE_NAME)"
|
||||
$(hide) $(PRIVATE_BUILD_EXECUTABLE)
|
||||
|
||||
ALL_EXECUTABLES += $(LOCAL_BUILT_MODULE)
|
||||
endif
|
||||
|
||||
#
|
||||
# If this is a copyable prebuilt module
|
||||
#
|
||||
ifeq ($(call module-is-copyable,$(LOCAL_MODULE)),$(true))
|
||||
$(LOCAL_BUILT_MODULE): $(LOCAL_OBJECTS)
|
||||
$(call host-echo-build-step,$(PRIVATE_ABI),Prebuilt) "$(PRIVATE_NAME) <= $(call pretty-dir,$(dir $<))"
|
||||
$(hide) $(call host-cp,$<,$@)
|
||||
endif
|
||||
|
||||
ifeq ($(LOCAL_STRIP_MODE),)
|
||||
NDK_STRIP_MODE := $(NDK_APP_STRIP_MODE)
|
||||
else
|
||||
NDK_STRIP_MODE := $(LOCAL_STRIP_MODE)
|
||||
endif
|
||||
|
||||
#
|
||||
# If this is an installable module
|
||||
#
|
||||
ifeq ($(call module-is-installable,$(LOCAL_MODULE)),$(true))
|
||||
$(LOCAL_INSTALLED): PRIVATE_ABI := $(TARGET_ARCH_ABI)
|
||||
$(LOCAL_INSTALLED): PRIVATE_NAME := $(notdir $(LOCAL_BUILT_MODULE))
|
||||
$(LOCAL_INSTALLED): PRIVATE_SRC := $(LOCAL_BUILT_MODULE)
|
||||
$(LOCAL_INSTALLED): PRIVATE_DST_DIR := $(NDK_APP_DST_DIR)
|
||||
$(LOCAL_INSTALLED): PRIVATE_DST := $(LOCAL_INSTALLED)
|
||||
$(LOCAL_INSTALLED): PRIVATE_STRIP := $(TARGET_STRIP)
|
||||
$(LOCAL_INSTALLED): PRIVATE_STRIP_MODE := $(NDK_STRIP_MODE)
|
||||
$(LOCAL_INSTALLED): PRIVATE_STRIP_CMD := $(call cmd-strip, $(PRIVATE_DST))
|
||||
|
||||
$(LOCAL_INSTALLED): $(LOCAL_BUILT_MODULE) clean-installed-binaries
|
||||
$(call host-echo-build-step,$(PRIVATE_ABI),Install) "$(PRIVATE_NAME) => $(call pretty-dir,$(PRIVATE_DST))"
|
||||
$(hide) $(call host-install,$(PRIVATE_SRC),$(PRIVATE_DST))
|
||||
$(if $(filter none,$(PRIVATE_STRIP_MODE)),,$(hide) $(PRIVATE_STRIP_CMD))
|
||||
|
||||
$(call generate-file-dir,$(LOCAL_INSTALLED))
|
||||
|
||||
endif
|
||||
31
Android/android-ndk-r27d/build/core/build-executable.mk
Normal file
@ -0,0 +1,31 @@
|
||||
# Copyright (C) 2009 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# this file is included from Android.mk files to build a target-specific
|
||||
# executable program
|
||||
#
|
||||
|
||||
LOCAL_BUILD_SCRIPT := BUILD_EXECUTABLE
|
||||
LOCAL_MAKEFILE := $(local-makefile)
|
||||
|
||||
$(call check-defined-LOCAL_MODULE,$(LOCAL_BUILD_SCRIPT))
|
||||
$(call check-LOCAL_MODULE,$(LOCAL_MAKEFILE))
|
||||
$(call check-LOCAL_MODULE_FILENAME)
|
||||
|
||||
$(call handle-module-filename,,)
|
||||
$(call handle-module-built)
|
||||
|
||||
LOCAL_MODULE_CLASS := EXECUTABLE
|
||||
include $(BUILD_SYSTEM)/build-module.mk
|
||||
225
Android/android-ndk-r27d/build/core/build-local.mk
Normal file
@ -0,0 +1,225 @@
|
||||
# Copyright (C) 2010 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# This file is designed to be called from the 'ndk-build' script
|
||||
# or similar wrapper tool.
|
||||
#
|
||||
|
||||
# Detect the NDK installation path by processing this Makefile's location.
|
||||
# This assumes we are located under $NDK_ROOT/build/core/main.mk
|
||||
#
|
||||
|
||||
# Don't output to stdout if we're being invoked to dump a variable
|
||||
DUMP_VAR := $(patsubst DUMP_%,%,$(filter DUMP_%,$(MAKECMDGOALS)))
|
||||
ifneq (,$(DUMP_VAR))
|
||||
NDK_NO_INFO := 1
|
||||
NDK_NO_WARNINGS := 1
|
||||
endif
|
||||
|
||||
NDK_ROOT := $(dir $(lastword $(MAKEFILE_LIST)))
|
||||
NDK_ROOT := $(subst \,/,$(NDK_ROOT))
|
||||
NDK_ROOT := $(strip $(NDK_ROOT:%build/core/=%))
|
||||
NDK_ROOT := $(NDK_ROOT:%/=%)
|
||||
ifeq ($(NDK_ROOT),)
|
||||
# for the case when we're invoked from the NDK install path
|
||||
NDK_ROOT := .
|
||||
endif
|
||||
ifeq ($(NDK_LOG),1)
|
||||
$(info Android NDK: NDK installation path auto-detected: '$(NDK_ROOT)')
|
||||
endif
|
||||
ifneq ($(words $(NDK_ROOT)),1)
|
||||
$(info Android NDK: Your NDK installation path contains spaces.)
|
||||
$(info Android NDK: Please re-install to a different location to fix the issue !)
|
||||
$(error Aborting.)
|
||||
endif
|
||||
|
||||
include $(NDK_ROOT)/build/core/init.mk
|
||||
|
||||
# ====================================================================
|
||||
#
|
||||
# If NDK_PROJECT_PATH is not defined, find the application's project
|
||||
# path by looking at the manifest file in the current directory or
|
||||
# any of its parents. If none is found, try again with 'jni/Android.mk'
|
||||
#
|
||||
# Note that we first look at the current directory to avoid using
|
||||
# absolute NDK_PROJECT_PATH values. This reduces the length of all
|
||||
# source, object and binary paths that are passed to build commands.
|
||||
#
|
||||
# It turns out that some people use ndk-build to generate static
|
||||
# libraries without a full Android project tree.
|
||||
#
|
||||
# If NDK_PROJECT_PATH=null, ndk-build make no attempt to look for it, but does
|
||||
# need the following variables depending on NDK_PROJECT_PATH to be explicitly
|
||||
# specified (from the default, if any):
|
||||
#
|
||||
# NDK_OUT
|
||||
# NDK_LIBS_OUT
|
||||
# APP_BUILD_SCRIPT
|
||||
# NDK_DEBUG (optional, default to 0)
|
||||
# Other APP_* used to be in Application.mk
|
||||
#
|
||||
# This behavior may be useful in an integrated build system.
|
||||
#
|
||||
# ====================================================================
|
||||
|
||||
find-project-dir = $(strip $(call find-project-dir-inner,$(abspath $1),$2))
|
||||
|
||||
find-project-dir-inner = \
|
||||
$(eval __found_project_path := )\
|
||||
$(eval __find_project_path := $1)\
|
||||
$(eval __find_project_file := $2)\
|
||||
$(call find-project-dir-inner-2)\
|
||||
$(__found_project_path)
|
||||
|
||||
find-project-dir-inner-2 = \
|
||||
$(call ndk_log,Looking for $(__find_project_file) in $(__find_project_path))\
|
||||
$(eval __find_project_manifest := $(strip $(wildcard $(__find_project_path)/$(__find_project_file))))\
|
||||
$(if $(__find_project_manifest),\
|
||||
$(call ndk_log, Found it !)\
|
||||
$(eval __found_project_path := $(__find_project_path))\
|
||||
,\
|
||||
$(eval __find_project_parent := $(call parent-dir,$(__find_project_path)))\
|
||||
$(if $(__find_project_parent),\
|
||||
$(eval __find_project_path := $(__find_project_parent))\
|
||||
$(call find-project-dir-inner-2)\
|
||||
)\
|
||||
)
|
||||
|
||||
NDK_PROJECT_PATH := $(strip $(NDK_PROJECT_PATH))
|
||||
APP_PROJECT_PATH := $(strip $(APP_PROJECT_PATH))
|
||||
|
||||
ifneq (,$(APP_PROJECT_PATH))
|
||||
ifeq (,$(NDK_PROJECT_PATH))
|
||||
# If NDK_PROJECT_PATH isn't set and APP_PROJECT_PATH is present, use APP_PROJECT_PATH
|
||||
$(call ndk_log,Use APP_PROJECT_PATH for NDK_PROJECT_PATH: $(APP_PROJECT_PATH))
|
||||
NDK_PROJECT_PATH := $(APP_PROJECT_PATH)
|
||||
else
|
||||
# If both NDK_PROJECT_PATH and APP_PROJECT_PATH are present, check consistency
|
||||
ifneq ($(NDK_PROJECT_PATH),$(APP_PROJECT_PATH))
|
||||
$(call __ndk_info,WARNING: NDK_PROJECT_PATH and APP_PROJECT_PATH are both set but not equal literally)
|
||||
$(call __ndk_info, NDK_PROJECT_PATH = $(NDK_PROJECT_PATH))
|
||||
$(call __ndk_info, APP_PROJECT_PATH = $(APP_PROJECT_PATH))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq (null,$(NDK_PROJECT_PATH))
|
||||
|
||||
$(call ndk_log,Make no attempt to look for NDK_PROJECT_PATH.)
|
||||
|
||||
else
|
||||
|
||||
# To keep paths as short as possible during the build, we first look if the
|
||||
# current directory is the top of our project path. If this is the case, we
|
||||
# will define NDK_PROJECT_PATH to simply '.'
|
||||
#
|
||||
# Otherwise, we will use find-project-dir which will first get the absolute
|
||||
# path of the current directory the climb back the hierarchy until we find
|
||||
# something. The result will always be a much longer definition for
|
||||
# NDK_PROJECT_PATH
|
||||
#
|
||||
ifndef NDK_PROJECT_PATH
|
||||
ifneq (,$(strip $(wildcard AndroidManifest.xml)))
|
||||
NDK_PROJECT_PATH := .
|
||||
else
|
||||
ifneq (,$(strip $(wildcard jni/Android.mk)))
|
||||
NDK_PROJECT_PATH := .
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
ifndef NDK_PROJECT_PATH
|
||||
NDK_PROJECT_PATH := $(call find-project-dir,.,jni/Android.mk)
|
||||
endif
|
||||
ifndef NDK_PROJECT_PATH
|
||||
NDK_PROJECT_PATH := $(call find-project-dir,.,AndroidManifest.xml)
|
||||
endif
|
||||
ifndef NDK_PROJECT_PATH
|
||||
$(call __ndk_info,Could not find application project directory !)
|
||||
$(call __ndk_info,Please define the NDK_PROJECT_PATH variable to point to it.)
|
||||
$(call __ndk_error,Aborting)
|
||||
endif
|
||||
|
||||
# Check that there are no spaces in the project path, or bad things will happen
|
||||
ifneq ($(words $(NDK_PROJECT_PATH)),1)
|
||||
$(call __ndk_info,Your Android application project path contains spaces: '$(NDK_PROJECT_PATH)')
|
||||
$(call __ndk_info,The Android NDK build cannot work here. Please move your project to a different location.)
|
||||
$(call __ndk_error,Aborting.)
|
||||
endif
|
||||
|
||||
$(call ndk_log,Found project path: $(NDK_PROJECT_PATH))
|
||||
|
||||
NDK_APPLICATION_MK := $(strip $(wildcard $(NDK_PROJECT_PATH)/jni/Application.mk))
|
||||
|
||||
endif # NDK_PROJECT_PATH == null
|
||||
|
||||
ifndef NDK_APPLICATION_MK
|
||||
NDK_APPLICATION_MK := $(NDK_ROOT)/build/core/default-application.mk
|
||||
endif
|
||||
|
||||
|
||||
# Place all generated intermediate files here
|
||||
NDK_APP_OUT := $(strip $(NDK_OUT))
|
||||
ifndef NDK_APP_OUT
|
||||
ifeq (null,$(NDK_PROJECT_PATH))
|
||||
$(call __ndk_info,NDK_PROJECT_PATH==null. Please explicitly set NDK_OUT to directory for all generated intermediate files.)
|
||||
$(call __ndk_error,Aborting.)
|
||||
endif
|
||||
NDK_APP_OUT := $(NDK_PROJECT_PATH)/obj
|
||||
endif
|
||||
$(call ndk_log,Ouput path for intermediate files: $(NDK_APP_OUT))
|
||||
|
||||
# Place all generated library files here. This is rarely changed since aapt expects the default libs/
|
||||
NDK_APP_LIBS_OUT := $(strip $(NDK_LIBS_OUT))
|
||||
ifndef NDK_APP_LIBS_OUT
|
||||
ifeq (null,$(NDK_PROJECT_PATH))
|
||||
$(call __ndk_info,NDK_PROJECT_PATH==null. Please explicitly set NDK_LIBS_OUT to directory for generated library files.)
|
||||
$(call __ndk_error,Aborting.)
|
||||
endif
|
||||
NDK_APP_LIBS_OUT := $(NDK_PROJECT_PATH)/libs
|
||||
endif
|
||||
$(call ndk_log,Ouput path for generated library files: $(NDK_APP_LIBS_OUT))
|
||||
|
||||
# Fake an application named 'local'
|
||||
_app := local
|
||||
_application_mk := $(NDK_APPLICATION_MK)
|
||||
NDK_APPS := $(_app)
|
||||
|
||||
include $(BUILD_SYSTEM)/add-application.mk
|
||||
|
||||
# If a goal is DUMP_xxx then we dump a variable xxx instead
|
||||
# of building anything
|
||||
#
|
||||
MAKECMDGOALS := $(filter-out DUMP_$(DUMP_VAR),$(MAKECMDGOALS))
|
||||
|
||||
include $(BUILD_SYSTEM)/setup-imports.mk
|
||||
|
||||
ifneq (,$(DUMP_VAR))
|
||||
|
||||
# We only support a single DUMP_XXX goal at a time for now.
|
||||
ifneq ($(words $(DUMP_VAR)),1)
|
||||
$(call __ndk_error,!!TOO-MANY-DUMP-VARIABLES!!)
|
||||
endif
|
||||
|
||||
$(foreach _app,$(NDK_APPS),\
|
||||
$(eval include $(BUILD_SYSTEM)/setup-app.mk)\
|
||||
)
|
||||
|
||||
.PHONY : DUMP_$(DUMP_VAR)
|
||||
DUMP_$(DUMP_VAR):
|
||||
@echo $($(DUMP_VAR))
|
||||
else
|
||||
# Build it
|
||||
include $(BUILD_SYSTEM)/build-all.mk
|
||||
endif
|
||||