mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 08:15:59 -10:00
Android Build (#6992)
* android build support * Update es_compatibility.h * Support reporting flags in android * include pref_path in json v2 * add vulkan headers * shaderc includes * remove headers * force use prebuilt vulkan * fix vulkan inverted screen * fix find sdl3 precompiled android * Update CMakeLists.txt * replace sdl2 android function names for sdl3 versions * change sdl includes in osapi * fix sdl3 includes, again * rename function to sdl3 name * remove unused definition * copy libshaderc_shared.so to output in android * do not look for vulkan-loader in android * revert changes to SDL2.cmake * add comment to vulkan pretransform setup * cleanup maxsize function * rename new android functions in osapi * android trap back button * Remove calculate_max_resize_max_size * touch ui opacity control * change opacity control to int * fix opacity slider * Corrected android working folder fallback and set borderless mode as default * Implement shared helper for JNI calls * Add version tag and sha256 to android prebuilt libs * vulkan android check eSuboptimal * An already requested rebuild also avoids redundant Android surface queries * add cache bool to cmakelist * set string ids
This commit is contained in:
1 parent
705d115214
commit
1d0dd687cd
24 files changed
+498
-107
No files matched your search
+11
-2
@@ -57,6 +57,15 @@ endif()
|
||||
|
||||
PROJECT(FS2_Open LANGUAGES ${FSO_LANGUAGES})
|
||||
|
||||
if (ANDROID)
|
||||
message(STATUS "Building for Android (NDK)")
|
||||
set(FSO_BUILD_INCLUDED_LIBS ON CACHE BOOL "" FORCE)
|
||||
set(USING_PREBUILT_LIBS ON CACHE BOOL "" FORCE)
|
||||
set(SDL3_USE_PRECOMPILED ON CACHE BOOL "" FORCE)
|
||||
set(OPENAL_USE_PRECOMPILED ON CACHE BOOL "" FORCE)
|
||||
set(USING_PREBUILT_VULKAN ON CACHE BOOL "" FORCE)
|
||||
endif()
|
||||
|
||||
# Check if the external modules exists
|
||||
IF(NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib/libRocket/Build")
|
||||
message(FATAL_ERROR "External submodules could not be found. Please make sure you have updated your submodules.")
|
||||
@@ -109,8 +118,8 @@ OPTION(FSO_BUILD_TESTS "Build unit tests" OFF)
|
||||
|
||||
OPTION(FSO_DEVELOPMENT_MODE "Generate binaries in development mode, only use if you know what you're doing!" OFF)
|
||||
|
||||
IF(WIN32 OR APPLE)
|
||||
# On windows and mac the default should be to always build the included libraries
|
||||
IF(WIN32 OR APPLE OR ANDROID)
|
||||
# On windows, mac and android the default should be to always build the included libraries
|
||||
SET(FSO_BUILD_INCLUDED_LIBS_DEFAULT ON)
|
||||
ELSE()
|
||||
SET(FSO_BUILD_INCLUDED_LIBS_DEFAULT OFF)
|
||||
|
||||
@@ -185,7 +185,12 @@ int cfile_init(const char *exe_dir, const char *cdrom_dir)
|
||||
|
||||
char buf[CFILE_ROOT_DIRECTORY_LEN];
|
||||
|
||||
strncpy(buf, exe_dir, CFILE_ROOT_DIRECTORY_LEN - 1);
|
||||
#ifndef __ANDROID__
|
||||
strncpy(buf, exe_dir, CFILE_ROOT_DIRECTORY_LEN - 1);
|
||||
#else
|
||||
(void)exe_dir;
|
||||
strncpy(buf, os_android_get_working_folder_path().c_str(), CFILE_ROOT_DIRECTORY_LEN - 1);
|
||||
#endif
|
||||
|
||||
buf[CFILE_ROOT_DIRECTORY_LEN - 1] = '\0';
|
||||
|
||||
|
||||
+85
-50
@@ -75,6 +75,7 @@ const char *cmdline_arg_types[] =
|
||||
enum class flag_output_type {
|
||||
Binary,
|
||||
Json_V1,
|
||||
Json_V2,
|
||||
};
|
||||
|
||||
// variables
|
||||
@@ -555,7 +556,12 @@ int Cmdline_show_pos = 0;
|
||||
int Cmdline_show_stats = 0;
|
||||
int Cmdline_save_render_targets = 0;
|
||||
bool Cmdline_window = false;
|
||||
#ifdef __ANDROID__
|
||||
// Default to borderless in the legacy window-mode path as well.
|
||||
bool Cmdline_fullscreen_window = true;
|
||||
#else
|
||||
bool Cmdline_fullscreen_window = false;
|
||||
#endif
|
||||
std::optional<std::pair<uint16_t, uint16_t>>Cmdline_window_res = std::nullopt;
|
||||
char *Cmdline_res = 0;
|
||||
char *Cmdline_center_res = 0;
|
||||
@@ -1358,51 +1364,58 @@ static void handle_unix_modlist(char **modlist, size_t *len)
|
||||
|
||||
// external entry point into this modules
|
||||
|
||||
static void json_add_version(json_t* root) {
|
||||
auto version_obj = json_object();
|
||||
|
||||
json_object_set_new(version_obj, "full", json_string(FS_VERSION_FULL));
|
||||
json_object_set_new(version_obj, "major", json_integer(FS_VERSION_MAJOR));
|
||||
json_object_set_new(version_obj, "minor", json_integer(FS_VERSION_MINOR));
|
||||
json_object_set_new(version_obj, "build", json_integer(FS_VERSION_BUILD));
|
||||
|
||||
json_object_set_new(version_obj, "has_revision", json_boolean(FS_VERSION_HAS_REVIS));
|
||||
json_object_set_new(version_obj, "revision", json_integer(FS_VERSION_REVIS));
|
||||
json_object_set_new(version_obj, "revision_str", json_string(FS_VERSION_REVIS_STR));
|
||||
|
||||
json_object_set_new(root, "version", version_obj);
|
||||
}
|
||||
|
||||
static void json_add_easy_flags(json_t* root) {
|
||||
auto easy_array = json_array();
|
||||
|
||||
for (auto& easy_flag : easy_flags) {
|
||||
json_array_append_new(easy_array, json_string(easy_flag.name));
|
||||
}
|
||||
|
||||
json_object_set_new(root, "easy_flags", easy_array);
|
||||
}
|
||||
|
||||
static void json_add_flags(json_t* root) {
|
||||
auto flags_array = json_array();
|
||||
|
||||
for (auto& flag : exe_params) {
|
||||
auto flag_obj = json_object();
|
||||
|
||||
json_object_set_new(flag_obj, "name", json_string(flag.name));
|
||||
json_object_set_new(flag_obj, "description", json_string(flag.desc));
|
||||
json_object_set_new(flag_obj, "fso_only", json_boolean(flag.fso_only));
|
||||
json_object_set_new(flag_obj, "on_flags", json_integer(flag.on_flags));
|
||||
json_object_set_new(flag_obj, "off_flags", json_integer(flag.off_flags));
|
||||
json_object_set_new(flag_obj, "type", json_string(flag.type));
|
||||
json_object_set_new(flag_obj, "web_url", json_string(flag.web_url));
|
||||
|
||||
json_array_append_new(flags_array, flag_obj);
|
||||
}
|
||||
|
||||
json_object_set_new(root, "flags", flags_array);
|
||||
}
|
||||
|
||||
static json_t* json_get_v1() {
|
||||
auto root = json_object();
|
||||
|
||||
{
|
||||
auto version_obj = json_object();
|
||||
json_add_version(root);
|
||||
json_add_easy_flags(root);
|
||||
json_add_flags(root);
|
||||
|
||||
json_object_set_new(version_obj, "full", json_string(FS_VERSION_FULL));
|
||||
json_object_set_new(version_obj, "major", json_integer(FS_VERSION_MAJOR));
|
||||
json_object_set_new(version_obj, "minor", json_integer(FS_VERSION_MINOR));
|
||||
json_object_set_new(version_obj, "build", json_integer(FS_VERSION_BUILD));
|
||||
|
||||
json_object_set_new(version_obj, "has_revision", json_boolean(FS_VERSION_HAS_REVIS));
|
||||
json_object_set_new(version_obj, "revision", json_integer(FS_VERSION_REVIS));
|
||||
json_object_set_new(version_obj, "revision_str", json_string(FS_VERSION_REVIS_STR));
|
||||
|
||||
json_object_set_new(root, "version", version_obj);
|
||||
}
|
||||
{
|
||||
auto easy_array = json_array();
|
||||
|
||||
for (auto& easy_flag : easy_flags) {
|
||||
json_array_append_new(easy_array, json_string(easy_flag.name));
|
||||
}
|
||||
|
||||
json_object_set_new(root, "easy_flags", easy_array);
|
||||
}
|
||||
{
|
||||
auto flags_array = json_array();
|
||||
|
||||
for (auto& flag : exe_params) {
|
||||
auto flag_obj = json_object();
|
||||
|
||||
json_object_set_new(flag_obj, "name", json_string(flag.name));
|
||||
json_object_set_new(flag_obj, "description", json_string(flag.desc));
|
||||
json_object_set_new(flag_obj, "fso_only", json_boolean(flag.fso_only));
|
||||
json_object_set_new(flag_obj, "on_flags", json_integer(flag.on_flags));
|
||||
json_object_set_new(flag_obj, "off_flags", json_integer(flag.off_flags));
|
||||
json_object_set_new(flag_obj, "type", json_string(flag.type));
|
||||
json_object_set_new(flag_obj, "web_url", json_string(flag.web_url));
|
||||
|
||||
json_array_append_new(flags_array, flag_obj);
|
||||
}
|
||||
|
||||
json_object_set_new(root, "flags", flags_array);
|
||||
}
|
||||
{
|
||||
auto caps_array = json_array();
|
||||
|
||||
@@ -1513,6 +1526,19 @@ static json_t* json_get_v1() {
|
||||
return root;
|
||||
}
|
||||
|
||||
// json_v2 reports version, supported flags, and the preferences path for launchers,
|
||||
// without querying graphics, audio, or joystick devices.
|
||||
static json_t* json_get_v2() {
|
||||
auto root = json_object();
|
||||
|
||||
json_add_version(root);
|
||||
json_add_easy_flags(root);
|
||||
json_add_flags(root);
|
||||
json_object_set_new(root, "pref_path", json_string(os_get_config_path().c_str()));
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
static void write_flags_file() {
|
||||
FILE *fp = fopen("flags.lch","w");
|
||||
|
||||
@@ -1570,6 +1596,8 @@ static flag_output_type get_flags_output_type() {
|
||||
return flag_output_type::Binary;
|
||||
} else if (type == "json_v1") {
|
||||
return flag_output_type::Json_V1;
|
||||
} else if (type == "json_v2") {
|
||||
return flag_output_type::Json_V2;
|
||||
} else {
|
||||
// This is supposed to make it easy for the launcher to recognize an unsupported type
|
||||
printf("OUTPUT TYPE NOT SUPPORTED!\n");
|
||||
@@ -1580,17 +1608,23 @@ static flag_output_type get_flags_output_type() {
|
||||
|
||||
static void write_flags() {
|
||||
auto type = get_flags_output_type();
|
||||
switch(type) {
|
||||
case flag_output_type::Binary:
|
||||
write_flags_file();
|
||||
break;
|
||||
case flag_output_type::Json_V1:
|
||||
json_t* root = json_get_v1();
|
||||
|
||||
json_dumpf(root, stdout, JSON_INDENT(4));
|
||||
json_decref(root);
|
||||
break;
|
||||
if (type == flag_output_type::Binary) {
|
||||
write_flags_file();
|
||||
return;
|
||||
}
|
||||
|
||||
json_t* root = (type == flag_output_type::Json_V1) ? json_get_v1() : json_get_v2();
|
||||
|
||||
#ifdef __ANDROID__
|
||||
char* dumped = json_dumps(root, JSON_INDENT(4));
|
||||
os_android_set_flags_string(dumped ? dumped : "");
|
||||
free(dumped);
|
||||
#else
|
||||
json_dumpf(root, stdout, JSON_INDENT(4));
|
||||
#endif
|
||||
|
||||
json_decref(root);
|
||||
}
|
||||
|
||||
bool SetCmdlineParams()
|
||||
@@ -1787,6 +1821,7 @@ bool SetCmdlineParams()
|
||||
options::OptionsManager::instance()->setOverride("Graphics.WindowMode",
|
||||
std::unique_ptr<json_t>(json_integer(static_cast<int>(os::ViewportState::Windowed))), "-window");
|
||||
Cmdline_window = true;
|
||||
Cmdline_fullscreen_window = false;
|
||||
}
|
||||
|
||||
if ( fullscreen_window_arg.found( ) )
|
||||
|
||||
@@ -28,6 +28,23 @@ protected:
|
||||
return FALSE;
|
||||
|
||||
m_library = SDL_LoadObject(externlib);
|
||||
|
||||
#ifdef __ANDROID__
|
||||
if (m_library == NULL) {
|
||||
auto android_path = SDL_GetAndroidInternalStoragePath();
|
||||
if (android_path != nullptr) {
|
||||
SCP_string android_full_path = android_path;
|
||||
if (android_full_path.back() != DIR_SEPARATOR_CHAR) {
|
||||
android_full_path += DIR_SEPARATOR_CHAR;
|
||||
}
|
||||
android_full_path += SCP_string("natives") + DIR_SEPARATOR_CHAR + externlib;
|
||||
#ifndef NDEBUG
|
||||
mprintf(("Calling SDL_LoadObject with the following path: '%s'\n", android_full_path.c_str()));
|
||||
#endif
|
||||
m_library = SDL_LoadObject(android_full_path.c_str());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// check full path as a fallback for our own libraries
|
||||
// NOTE: basePath is assumed to have a trailing slash!
|
||||
|
||||
@@ -289,7 +289,11 @@ GraphicsAPI gr_get_configured_render_api()
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef __ANDROID__
|
||||
os::ViewportState Gr_configured_window_state = os::ViewportState::Fullscreen;
|
||||
#else
|
||||
os::ViewportState Gr_configured_window_state = os::ViewportState::Borderless;
|
||||
#endif
|
||||
|
||||
static bool mode_change_func(os::ViewportState state, bool initial)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <direct.h>
|
||||
#endif
|
||||
|
||||
#if !defined __APPLE_CC__ && defined SCP_UNIX
|
||||
#if !defined __APPLE_CC__ && !defined __ANDROID__ && defined SCP_UNIX
|
||||
#include<glad/glad_glx.h>
|
||||
//Required because X defines none and always, which is used later
|
||||
#undef None
|
||||
@@ -1394,7 +1394,7 @@ bool gr_opengl_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps)
|
||||
Error(LOCATION, "Failed to load OpenGL!");
|
||||
}
|
||||
|
||||
#if !defined __APPLE_CC__ && defined SCP_UNIX
|
||||
#if !defined __APPLE_CC__ && !defined __ANDROID__ && defined SCP_UNIX
|
||||
if (!gladLoadGLXLoader(GL_context->getLoaderFunction(), nullptr, 0)) {
|
||||
Error(LOCATION, "Failed to load GLX!");
|
||||
}
|
||||
|
||||
@@ -456,6 +456,7 @@ class VulkanRenderer {
|
||||
void acquireNextSwapChainImage();
|
||||
|
||||
bool recreateSwapChain();
|
||||
bool shouldRecreateSwapChain(SwapChainStatus status) const;
|
||||
|
||||
void createImGuiDescriptorPool();
|
||||
void initImGui();
|
||||
@@ -480,6 +481,7 @@ class VulkanRenderer {
|
||||
bool m_hdrActive = false; // True when an HDR10 (PQ/BT.2020) swap chain was negotiated
|
||||
bool m_hdrMetadataSupported = false; // VK_EXT_hdr_metadata device extension enabled
|
||||
vk::Extent2D m_swapChainExtent;
|
||||
vk::SurfaceTransformFlagBitsKHR m_swapChainPreTransform = vk::SurfaceTransformFlagBitsKHR::eIdentity;
|
||||
// The resolution the engine draws at (gr_screen.max_w/max_h), which is not
|
||||
// always the window size: when Cmdline_window_res is set,
|
||||
// SDLGraphicsOperations::createViewport sizes the window from that instead,
|
||||
|
||||
@@ -81,7 +81,7 @@ void VulkanRenderer::acquireNextSwapChainImage()
|
||||
MAX_ACQUIRE_ATTEMPTS);
|
||||
}
|
||||
|
||||
if (status == SwapChainStatus::eSuboptimal) {
|
||||
if (shouldRecreateSwapChain(status)) {
|
||||
m_swapChainNeedsRecreation = true;
|
||||
}
|
||||
|
||||
@@ -221,7 +221,7 @@ void VulkanRenderer::flip()
|
||||
auto presentStatus = m_frames[m_currentFrame]->submitAndPresent(m_currentCommandBuffers,
|
||||
m_renderingFinishedSemaphores[m_currentSwapChainImage].get());
|
||||
|
||||
if (presentStatus == SwapChainStatus::eSuboptimal || presentStatus == SwapChainStatus::eOutOfDate) {
|
||||
if (shouldRecreateSwapChain(presentStatus)) {
|
||||
m_swapChainNeedsRecreation = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -326,17 +326,17 @@ vk::Extent2D windowExtent()
|
||||
return {static_cast<uint32_t>(gr_screen.max_w), static_cast<uint32_t>(gr_screen.max_h)};
|
||||
}
|
||||
|
||||
vk::Extent2D chooseSwapChainExtent(const PhysicalDeviceValues& values, uint32_t width, uint32_t height)
|
||||
vk::Extent2D chooseSwapChainExtent(const vk::SurfaceCapabilitiesKHR& capabilities, uint32_t width, uint32_t height)
|
||||
{
|
||||
if (values.surfaceCapabilities.currentExtent.width != UINT32_MAX) {
|
||||
return values.surfaceCapabilities.currentExtent;
|
||||
if (capabilities.currentExtent.width != UINT32_MAX) {
|
||||
return capabilities.currentExtent;
|
||||
} else {
|
||||
VkExtent2D actualExtent = {width, height};
|
||||
|
||||
actualExtent.width = std::max(values.surfaceCapabilities.minImageExtent.width,
|
||||
std::min(values.surfaceCapabilities.maxImageExtent.width, actualExtent.width));
|
||||
actualExtent.height = std::max(values.surfaceCapabilities.minImageExtent.height,
|
||||
std::min(values.surfaceCapabilities.maxImageExtent.height, actualExtent.height));
|
||||
actualExtent.width = std::max(capabilities.minImageExtent.width,
|
||||
std::min(capabilities.maxImageExtent.width, actualExtent.width));
|
||||
actualExtent.height = std::max(capabilities.minImageExtent.height,
|
||||
std::min(capabilities.maxImageExtent.height, actualExtent.height));
|
||||
|
||||
return actualExtent;
|
||||
}
|
||||
@@ -1113,7 +1113,7 @@ bool VulkanRenderer::createSwapChain(const PhysicalDeviceValues& deviceValues, v
|
||||
createInfo.minImageCount = imageCount;
|
||||
createInfo.imageFormat = surfaceFormat.format;
|
||||
createInfo.imageColorSpace = surfaceFormat.colorSpace;
|
||||
createInfo.imageExtent = chooseSwapChainExtent(deviceValues, window.width, window.height);
|
||||
createInfo.imageExtent = chooseSwapChainExtent(deviceValues.surfaceCapabilities, window.width, window.height);
|
||||
createInfo.imageArrayLayers = 1;
|
||||
createInfo.imageUsage = vk::ImageUsageFlagBits::eColorAttachment
|
||||
| vk::ImageUsageFlagBits::eTransferSrc
|
||||
@@ -1128,7 +1128,19 @@ bool VulkanRenderer::createSwapChain(const PhysicalDeviceValues& deviceValues, v
|
||||
createInfo.imageSharingMode = vk::SharingMode::eExclusive;
|
||||
}
|
||||
|
||||
createInfo.preTransform = deviceValues.surfaceCapabilities.currentTransform;
|
||||
// preTransform describes the transform already applied to the rendered content.
|
||||
// FSO does not pre-rotate it, so request identity whenever supported. If this
|
||||
// differs from currentTransform (e.g. landscape on an Android phone), the
|
||||
// presentation engine compensates during presentation. Passing currentTransform
|
||||
// instead would claim that FSO had already performed that rotation.
|
||||
auto supported = deviceValues.surfaceCapabilities.supportedTransforms;
|
||||
if (supported & vk::SurfaceTransformFlagBitsKHR::eIdentity) {
|
||||
createInfo.preTransform = vk::SurfaceTransformFlagBitsKHR::eIdentity;
|
||||
}
|
||||
else {
|
||||
createInfo.preTransform = deviceValues.surfaceCapabilities.currentTransform;
|
||||
}
|
||||
|
||||
createInfo.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque;
|
||||
createInfo.presentMode = choosePresentMode(deviceValues);
|
||||
createInfo.clipped = true;
|
||||
@@ -1149,6 +1161,7 @@ bool VulkanRenderer::createSwapChain(const PhysicalDeviceValues& deviceValues, v
|
||||
m_hdrActive = (surfaceFormat.colorSpace == vk::ColorSpaceKHR::eHdr10St2084EXT);
|
||||
Gr_hdr_output_active = m_hdrActive;
|
||||
m_swapChainExtent = createInfo.imageExtent;
|
||||
m_swapChainPreTransform = createInfo.preTransform;
|
||||
|
||||
// Without -window_res the render resolution simply is the window, so follow
|
||||
// gr_screen (which gr_screen_resize() has already updated for this
|
||||
@@ -1210,6 +1223,42 @@ bool VulkanRenderer::createSwapChain(const PhysicalDeviceValues& deviceValues, v
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VulkanRenderer::shouldRecreateSwapChain(SwapChainStatus status) const
|
||||
{
|
||||
// An already requested rebuild also avoids redundant Android surface queries.
|
||||
if (m_swapChainNeedsRecreation || status == SwapChainStatus::eOutOfDate) {
|
||||
return true;
|
||||
}
|
||||
if (status != SwapChainStatus::eSuboptimal) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
if (m_swapChainPreTransform == vk::SurfaceTransformFlagBitsKHR::eIdentity) {
|
||||
vk::SurfaceCapabilitiesKHR capabilities;
|
||||
try {
|
||||
capabilities = m_physicalDevice.getSurfaceCapabilitiesKHR(m_vkSurface.get());
|
||||
} catch (const vk::SystemError&) {
|
||||
// Let the normal recreation path handle a failed surface query.
|
||||
return true;
|
||||
}
|
||||
if (capabilities.currentTransform != m_swapChainPreTransform &&
|
||||
(capabilities.supportedTransforms & m_swapChainPreTransform)) {
|
||||
const auto window = windowExtent();
|
||||
const auto extent = chooseSwapChainExtent(capabilities, window.width, window.height);
|
||||
if (extent == m_swapChainExtent) {
|
||||
// Android can report SUBOPTIMAL on every present while the compositor
|
||||
// compensates for our identity preTransform. Rebuilding with the same
|
||||
// extent and preTransform would just repeat that condition next frame.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VulkanRenderer::recreateSwapChain()
|
||||
{
|
||||
nprintf(("vulkan", "Vulkan: Recreating swap chain...\n"));
|
||||
@@ -1233,7 +1282,7 @@ bool VulkanRenderer::recreateSwapChain()
|
||||
|
||||
// Check for 0x0 extent (minimized window) — caller should retry later
|
||||
const vk::Extent2D window = windowExtent();
|
||||
auto extent = chooseSwapChainExtent(freshValues, window.width, window.height);
|
||||
auto extent = chooseSwapChainExtent(freshValues.surfaceCapabilities, window.width, window.height);
|
||||
if (extent.width == 0 || extent.height == 0) {
|
||||
nprintf(("vulkan", "Vulkan: Surface extent is 0x0 (minimized), deferring swap chain recreation\n"));
|
||||
return false;
|
||||
|
||||
@@ -65,7 +65,7 @@ bool *Lcl_unexpected_tstring_check = nullptr;
|
||||
// NOTE: with map storage of XSTR strings, the indexes no longer need to be contiguous,
|
||||
// but internal strings should still increment XSTR_SIZE to avoid collisions.
|
||||
// retail XSTR_SIZE = 1570
|
||||
// #define XSTR_SIZE 1936 // This is the next available ID
|
||||
// #define XSTR_SIZE 1940 // This is the next available ID
|
||||
|
||||
// struct to allow for strings.tbl-determined x offset
|
||||
// offset is 0 for english, by default
|
||||
|
||||
@@ -30,6 +30,14 @@
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <SDL3/SDL_system.h>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <jni.h>
|
||||
#include "options/Option.h"
|
||||
#include "utils/finally.h"
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
const char* ORGANIZATION_NAME = "HardLightProductions";
|
||||
@@ -925,3 +933,175 @@ SCP_string os_get_config_path(const SCP_string& subpath)
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/*
|
||||
Special functions for Android
|
||||
*/
|
||||
|
||||
#ifdef __ANDROID__
|
||||
static bool android_check_exception(JNIEnv* env, const char* operation)
|
||||
{
|
||||
if (!env->ExceptionCheck()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mprintf(("OSAPI: JNI exception in %s.\n", operation));
|
||||
env->ExceptionDescribe();
|
||||
env->ExceptionClear();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Callback>
|
||||
static void android_with_static_method(const char* name, const char* signature, Callback&& callback)
|
||||
{
|
||||
JNIEnv* env = static_cast<JNIEnv*>(SDL_GetAndroidJNIEnv());
|
||||
if (env == nullptr) {
|
||||
mprintf(("OSAPI: Couldn't get JNI environment for %s.\n", name));
|
||||
return;
|
||||
}
|
||||
if (android_check_exception(env, name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (env->PushLocalFrame(4) < 0) {
|
||||
android_check_exception(env, "PushLocalFrame");
|
||||
mprintf(("OSAPI: Couldn't create JNI local frame for %s.\n", name));
|
||||
return;
|
||||
}
|
||||
// Release the activity, class, and temporary strings on every exit path.
|
||||
auto local_frame = util::finally([env]() { env->PopLocalFrame(nullptr); });
|
||||
|
||||
jobject activity = static_cast<jobject>(SDL_GetAndroidActivity());
|
||||
if (android_check_exception(env, "SDL_GetAndroidActivity") || activity == nullptr) {
|
||||
mprintf(("OSAPI: Couldn't get Android activity for %s.\n", name));
|
||||
return;
|
||||
}
|
||||
|
||||
jclass activity_class = env->GetObjectClass(activity);
|
||||
if (android_check_exception(env, "GetObjectClass") || activity_class == nullptr) {
|
||||
mprintf(("OSAPI: Couldn't get Android activity class for %s.\n", name));
|
||||
return;
|
||||
}
|
||||
|
||||
jmethodID method = env->GetStaticMethodID(activity_class, name, signature);
|
||||
if (android_check_exception(env, name) || method == nullptr) {
|
||||
mprintf(("OSAPI: Couldn't find static method %s with signature %s.\n", name, signature));
|
||||
return;
|
||||
}
|
||||
|
||||
callback(env, activity_class, method);
|
||||
android_check_exception(env, name);
|
||||
}
|
||||
|
||||
void os_android_set_flags_string(const char* json)
|
||||
{
|
||||
android_with_static_method("setFlagsJson", "(Ljava/lang/String;)V",
|
||||
[json](JNIEnv* env, jclass activity_class, jmethodID method) {
|
||||
jstring value = env->NewStringUTF(json ? json : "");
|
||||
if (android_check_exception(env, "NewStringUTF") || value == nullptr) {
|
||||
return;
|
||||
}
|
||||
env->CallStaticVoidMethod(activity_class, method, value);
|
||||
});
|
||||
}
|
||||
|
||||
SCP_string os_android_get_working_folder_path()
|
||||
{
|
||||
SCP_string wfp{};
|
||||
android_with_static_method("getWorkingFolder", "()Ljava/lang/String;",
|
||||
[&wfp](JNIEnv* env, jclass activity_class, jmethodID method) {
|
||||
jstring value = static_cast<jstring>(env->CallStaticObjectMethod(activity_class, method));
|
||||
if (android_check_exception(env, "getWorkingFolder") || value == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const char* working_folder = env->GetStringUTFChars(value, nullptr);
|
||||
if (android_check_exception(env, "GetStringUTFChars") || working_folder == nullptr) {
|
||||
return;
|
||||
}
|
||||
auto release_chars = util::finally([env, value, working_folder]() {
|
||||
env->ReleaseStringUTFChars(value, working_folder);
|
||||
});
|
||||
wfp = working_folder;
|
||||
});
|
||||
|
||||
if (wfp.empty()) {
|
||||
mprintf(("Couldn't get working folder path from Java class, reverting to SDL default.\n"));
|
||||
// SDL's external storage path already includes the app's files directory.
|
||||
const char* fallbackPath = SDL_GetAndroidExternalStoragePath();
|
||||
if (fallbackPath) {
|
||||
wfp = SCP_string(fallbackPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure path ends with a dir separator
|
||||
if (!wfp.empty() && (wfp.back() != DIR_SEPARATOR_CHAR)) {
|
||||
wfp += DIR_SEPARATOR_CHAR;
|
||||
}
|
||||
mprintf(("Using working folder: %s\n", wfp.c_str()));
|
||||
return wfp;
|
||||
}
|
||||
|
||||
void os_android_touch_overlay_toggle(bool status)
|
||||
{
|
||||
android_with_static_method(status ? "enableOverlay" : "disableOverlay", "()V",
|
||||
[](JNIEnv* env, jclass activity_class, jmethodID method) {
|
||||
env->CallStaticVoidMethod(activity_class, method);
|
||||
});
|
||||
}
|
||||
|
||||
void os_android_touch_overlay_set_opacity(int opacity)
|
||||
{
|
||||
android_with_static_method("setOverlayOpacity", "(F)V",
|
||||
[opacity](JNIEnv* env, jclass activity_class, jmethodID method) {
|
||||
env->CallStaticVoidMethod(activity_class, method, static_cast<jfloat>(opacity) / 100.0f);
|
||||
});
|
||||
}
|
||||
|
||||
static bool touch_ui_change(bool new_val, bool initial)
|
||||
{
|
||||
if (initial) {
|
||||
return false;
|
||||
}
|
||||
os_android_touch_overlay_toggle(new_val);
|
||||
return true;
|
||||
}
|
||||
|
||||
static auto TouchOverlayOption = options::OptionBuilder<bool>("Input.TouchOverlay",
|
||||
std::pair<const char*, int>{"Touch Overlay", 1936},
|
||||
std::pair<const char*, int>{"Enable or disable the touch overlay", 1937})
|
||||
.category(std::make_pair("Input", 1827))
|
||||
.level(options::ExpertLevel::Beginner)
|
||||
.change_listener(touch_ui_change)
|
||||
.default_val(true)
|
||||
.importance(2)
|
||||
.finish();
|
||||
|
||||
static bool touch_ui_opacity_change(int new_val, bool initial)
|
||||
{
|
||||
Assertion(new_val >= 0 && new_val <= 100, "Invalid value %d supplied by options system!", new_val);
|
||||
if (initial) {
|
||||
return false;
|
||||
}
|
||||
os_android_touch_overlay_set_opacity(new_val);
|
||||
return true;
|
||||
}
|
||||
|
||||
static auto TouchOverlayOpacityOption = options::OptionBuilder<int>("Input.TouchOverlayOpacity",
|
||||
std::pair<const char*, int>{"Touch Overlay Opacity", 1938},
|
||||
std::pair<const char*, int>{"Set the opacity of the touch overlay", 1939})
|
||||
.category(std::make_pair("Input", 1827))
|
||||
.level(options::ExpertLevel::Beginner)
|
||||
.range(0, 100)
|
||||
.default_val(20)
|
||||
.flags({ options::OptionFlags::RangeTypeInteger })
|
||||
.change_listener(touch_ui_opacity_change)
|
||||
.importance(1)
|
||||
.finish();
|
||||
|
||||
void os_android_touch_overlay_init()
|
||||
{
|
||||
os_android_touch_overlay_toggle(TouchOverlayOption->getValue());
|
||||
os_android_touch_overlay_set_opacity(TouchOverlayOpacityOption->getValue());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -90,6 +90,23 @@ bool os_is_legacy_mode();
|
||||
*/
|
||||
SCP_string os_get_config_path(const SCP_string& subpath = "");
|
||||
|
||||
/*
|
||||
Special functions for Android
|
||||
*/
|
||||
#ifdef __ANDROID__
|
||||
// Get working folder absolute path from Android Java Class
|
||||
SCP_string os_android_get_working_folder_path();
|
||||
|
||||
// Calls to display the touch overlay depending on its last state
|
||||
void os_android_touch_overlay_init();
|
||||
|
||||
// Enable or disable the touch UI overlay
|
||||
void os_android_touch_overlay_toggle(bool status);
|
||||
|
||||
// Send the JSON flags string to the Android Java layer
|
||||
void os_android_set_flags_string(const char* json);
|
||||
#endif
|
||||
|
||||
namespace os
|
||||
{
|
||||
/**
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
#define _AL_H
|
||||
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if defined(__APPLE__) || defined(__ANDROID__)
|
||||
#include "al.h"
|
||||
#include "alc.h"
|
||||
#else
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#include "utils/unicode.h"
|
||||
#include "speech.h"
|
||||
#include <jni.h>
|
||||
#include "SDL.h"
|
||||
#include "SDL_system.h"
|
||||
#include <SDL3/SDL_system.h>
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
bool Speech_init = false;
|
||||
static jclass j_game_class = nullptr;
|
||||
@@ -41,8 +41,8 @@ bool speech_init()
|
||||
mprintf(("Speech : Try to init TTSManager on GameActivity...\n"));
|
||||
|
||||
// Get the JNI Environment pointer and current Activity instance via SDL
|
||||
JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
|
||||
jobject activity = (jobject)SDL_AndroidGetActivity();
|
||||
JNIEnv* env = (JNIEnv*)SDL_GetAndroidJNIEnv();
|
||||
jobject activity = (jobject)SDL_GetAndroidActivity();
|
||||
|
||||
if (env == nullptr) {
|
||||
mprintf(("Speech : Unable to get JNI environment!\n"));
|
||||
@@ -101,7 +101,7 @@ bool speech_play(const SCP_string& text)
|
||||
return false;
|
||||
}
|
||||
|
||||
JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
|
||||
JNIEnv* env = (JNIEnv*)SDL_GetAndroidJNIEnv();
|
||||
if (env == nullptr) {
|
||||
mprintf(("Speech : Unable to get JNI environment!\n"));
|
||||
return false;
|
||||
@@ -123,7 +123,7 @@ bool speech_stop()
|
||||
if (!Speech_init)
|
||||
return false;
|
||||
|
||||
JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
|
||||
JNIEnv* env = (JNIEnv*)SDL_GetAndroidJNIEnv();
|
||||
if (env == nullptr) {
|
||||
mprintf(("Speech : Unable to get JNI environment!\n"));
|
||||
return false;
|
||||
@@ -137,7 +137,7 @@ bool speech_pause()
|
||||
if (!Speech_init)
|
||||
return false;
|
||||
|
||||
JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
|
||||
JNIEnv* env = (JNIEnv*)SDL_GetAndroidJNIEnv();
|
||||
if (env == nullptr) {
|
||||
mprintf(("Speech : Unable to get JNI environment!\n"));
|
||||
return false;
|
||||
@@ -151,7 +151,7 @@ bool speech_resume()
|
||||
if (!Speech_init)
|
||||
return false;
|
||||
|
||||
JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
|
||||
JNIEnv* env = (JNIEnv*)SDL_GetAndroidJNIEnv();
|
||||
if (env == nullptr) {
|
||||
mprintf(("Speech : Unable to get JNI environment!\n"));
|
||||
return false;
|
||||
@@ -165,7 +165,7 @@ bool speech_is_speaking()
|
||||
if (!Speech_init)
|
||||
return false;
|
||||
|
||||
JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
|
||||
JNIEnv* env = (JNIEnv*)SDL_GetAndroidJNIEnv();
|
||||
if (env == nullptr) {
|
||||
mprintf(("Speech : Unable to get JNI environment!\n"));
|
||||
return false;
|
||||
@@ -179,7 +179,7 @@ void speech_deinit()
|
||||
if (!Speech_init)
|
||||
return;
|
||||
|
||||
JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
|
||||
JNIEnv* env = (JNIEnv*)SDL_GetAndroidJNIEnv();
|
||||
if (env != nullptr) {
|
||||
env->CallStaticVoidMethod(j_game_class, tts_shutdown);
|
||||
env->DeleteGlobalRef(j_game_class);
|
||||
@@ -217,7 +217,7 @@ bool speech_set_rate(float rate_percent)
|
||||
else if(rate_percent < 50.0)
|
||||
rate_percent = 50.0;
|
||||
|
||||
JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
|
||||
JNIEnv* env = (JNIEnv*)SDL_GetAndroidJNIEnv();
|
||||
if (env == nullptr) {
|
||||
mprintf(("Speech : Unable to get JNI environment!\n"));
|
||||
return false;
|
||||
@@ -233,7 +233,7 @@ bool speech_set_voice(int voice)
|
||||
if (!Speech_init)
|
||||
return false;
|
||||
|
||||
JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
|
||||
JNIEnv* env = (JNIEnv*)SDL_GetAndroidJNIEnv();
|
||||
if (env == nullptr) {
|
||||
mprintf(("Speech : Unable to get JNI environment!\n"));
|
||||
return false;
|
||||
@@ -263,7 +263,7 @@ SCP_vector<std::pair<int, SCP_string>> speech_enumerate_voices()
|
||||
if (!Speech_init)
|
||||
return voices;
|
||||
|
||||
JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
|
||||
JNIEnv* env = (JNIEnv*)SDL_GetAndroidJNIEnv();
|
||||
if (env == nullptr) {
|
||||
mprintf(("Speech : Unable to get JNI environment!\n"));
|
||||
return voices;
|
||||
|
||||
@@ -49,6 +49,7 @@ int filelength(int fd)
|
||||
|
||||
SCP_string dump_stacktrace()
|
||||
{
|
||||
#ifndef __ANDROID__
|
||||
#ifdef SCP_HAVE_EXECINFO_H
|
||||
// The following is adapted from here: https://panthema.net/2008/0901-stacktrace-demangled/
|
||||
const int ADDR_SIZE = 64;
|
||||
@@ -139,6 +140,9 @@ SCP_string dump_stacktrace()
|
||||
#else
|
||||
return "No stacktrace available";
|
||||
#endif
|
||||
#else
|
||||
return "No stacktrace available";
|
||||
#endif
|
||||
}
|
||||
|
||||
// retrieve the current working directory
|
||||
|
||||
@@ -19,7 +19,17 @@ IF(MSVC60)
|
||||
link_directories(${STLPORT_INCLUDE_LIB_DIRS})
|
||||
ENDIF(MSVC60)
|
||||
|
||||
ADD_EXECUTABLE(Freespace2 ${EXE_GUI_TYPE} ${FREESPACE_SRC})
|
||||
# For android, build a library not a executable
|
||||
if (ANDROID)
|
||||
add_library(Freespace2 SHARED ${FREESPACE_SRC})
|
||||
|
||||
# NDK base libs
|
||||
find_library(log-lib log)
|
||||
find_library(android-lib android)
|
||||
target_link_libraries(Freespace2 PRIVATE ${log-lib} ${android-lib})
|
||||
else()
|
||||
add_executable(Freespace2 ${EXE_GUI_TYPE} ${FREESPACE_SRC})
|
||||
endif()
|
||||
|
||||
target_compile_features(Freespace2 PUBLIC cxx_std_17)
|
||||
|
||||
@@ -27,9 +37,20 @@ include(resources.cmake)
|
||||
|
||||
SET_TARGET_PROPERTIES(Freespace2 PROPERTIES OUTPUT_NAME "fs2_open_${FSO_BINARY_SUFFIX}")
|
||||
|
||||
TARGET_LINK_LIBRARIES(Freespace2 code)
|
||||
TARGET_LINK_LIBRARIES(Freespace2 platform)
|
||||
TARGET_LINK_LIBRARIES(Freespace2 compiler)
|
||||
if (ANDROID)
|
||||
# one call
|
||||
target_link_libraries(Freespace2
|
||||
PRIVATE
|
||||
code
|
||||
platform
|
||||
compiler
|
||||
${ANDROID_LIBS}
|
||||
)
|
||||
else()
|
||||
TARGET_LINK_LIBRARIES(Freespace2 code)
|
||||
TARGET_LINK_LIBRARIES(Freespace2 platform)
|
||||
TARGET_LINK_LIBRARIES(Freespace2 compiler)
|
||||
endif()
|
||||
|
||||
if(FSO_BUILD_WITH_OPENGL_DEBUG)
|
||||
add_definitions(-DFS_OPENGL_DEBUG)
|
||||
@@ -46,6 +67,8 @@ IF(USE_STLPORT)
|
||||
INCLUDE_DIRECTORIES(BEFORE SYSTEM ${STLPORT_INCLUDE_DIRS})
|
||||
ENDIF(USE_STLPORT)
|
||||
|
||||
# Do not install when building android
|
||||
if (NOT ANDROID)
|
||||
# Add install script to copy the executable to the FreeSpace path if possible and wanted
|
||||
INSTALL(
|
||||
TARGETS Freespace2
|
||||
@@ -59,6 +82,7 @@ if (FSO_INSTALL_DEBUG_FILES)
|
||||
OPTIONAL)
|
||||
endif()
|
||||
endif()
|
||||
endif(NOT ANDROID)
|
||||
|
||||
if(FSO_BUILD_APPIMAGE)
|
||||
configure_file("${CMAKE_CURRENT_LIST_DIR}/cmake/AppRun.in" "${CMAKE_CURRENT_BINARY_DIR}/AppRun.gen" @ONLY)
|
||||
@@ -84,6 +108,8 @@ enable_clang_tidy(Freespace2)
|
||||
INCLUDE(util)
|
||||
COPY_FILES_TO_TARGET(Freespace2)
|
||||
|
||||
# Do not launch when building android
|
||||
if (NOT ANDROID)
|
||||
include(CreateLaunchers)
|
||||
create_target_launcher(Freespace2
|
||||
WORKING_DIRECTORY ${FSO_FREESPACE_PATH}
|
||||
@@ -96,6 +122,7 @@ add_custom_target(launch_fso
|
||||
WORKING_DIRECTORY "${FSO_FREESPACE_PATH}"
|
||||
COMMENT "Launching FreeSpace"
|
||||
VERBATIM)
|
||||
endif()
|
||||
|
||||
# Ensure that Xcode generates debug symbols on macOS for Debug builds
|
||||
set_target_properties(Freespace2 PROPERTIES XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYMBOLS[variant=Debug] "YES")
|
||||
|
||||
@@ -232,6 +232,12 @@ std::unique_ptr<os::Viewport> SDLGraphicsOperations::createViewport(const os::Vi
|
||||
windowflags |= SDL_WINDOW_MOUSE_GRABBED;
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
SDL_SetHint(SDL_HINT_ANDROID_TRAP_BACK_BUTTON, "1");
|
||||
SDL_SetHint(SDL_HINT_ORIENTATIONS, "LandscapeLeft LandscapeRight");
|
||||
os_android_touch_overlay_init();
|
||||
#endif
|
||||
|
||||
SDL_Rect bounds;
|
||||
if ( !SDL_GetDisplayBounds(props.display, &bounds) ) {
|
||||
mprintf(("Failed to get display bounds: %s\n", SDL_GetError()));
|
||||
|
||||
@@ -8301,3 +8301,10 @@ int main(int argc, char *argv[])
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
extern "C" int android_main(int argc, char* argv[])
|
||||
{
|
||||
return main(argc, argv);
|
||||
}
|
||||
#endif
|
||||
+3
-3
@@ -48,7 +48,7 @@ IF(PLATFORM_WINDOWS)
|
||||
target_link_libraries(ffmpeg INTERFACE swscale)
|
||||
target_link_libraries(ffmpeg INTERFACE swresample)
|
||||
ELSE(WIN32)
|
||||
if (PLATFORM_MAC)
|
||||
if (PLATFORM_MAC OR ANDROID)
|
||||
option(FFMPEG_USE_PRECOMPILED "Use precompiled version of FFmpeg. If disabled the system libraries will be used." ON)
|
||||
else()
|
||||
option(FFMPEG_USE_PRECOMPILED "Use precompiled version of FFmpeg. If disabled the system libraries will be used." OFF)
|
||||
@@ -97,8 +97,8 @@ ELSE(WIN32)
|
||||
# Use our libraries
|
||||
find_library(${name}_LOCATION ${name}
|
||||
PATHS "${FFMPEG_PATH}/lib"
|
||||
NO_DEFAULT_PATH)
|
||||
|
||||
NO_DEFAULT_PATH
|
||||
NO_CMAKE_FIND_ROOT_PATH)
|
||||
file(GLOB ${name}_LIBS "${FFMPEG_PATH}/lib/lib${name}*")
|
||||
|
||||
get_filename_component(FULL_LIB_PATH "${${name}_LOCATION}" REALPATH)
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ ELSE(WIN32)
|
||||
message(STATUS "Using pre-built OpenAL library.")
|
||||
|
||||
unset(OpenAL_LOCATION CACHE)
|
||||
find_library(OpenAL_LOCATION openal PATHS "${OpenAL_ROOT_DIR}/lib" NO_DEFAULT_PATH)
|
||||
find_library(OpenAL_LOCATION openal PATHS "${OpenAL_ROOT_DIR}/lib" NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH)
|
||||
|
||||
get_filename_component(FULL_LIB_PATH "${OpenAL_LOCATION}" REALPATH)
|
||||
ADD_IMPORTED_LIB(openal "${OpenAL_ROOT_DIR}/include" "${FULL_LIB_PATH}")
|
||||
|
||||
+1
-1
@@ -66,7 +66,7 @@ else()
|
||||
message(STATUS "Using pre-built SDL3 library.")
|
||||
|
||||
unset(SDL3_LOCATION CACHE)
|
||||
find_library(SDL3_LOCATION NAMES SDL3 SDL3-3.0 PATHS "${SDL3_ROOT_DIR}/lib" NO_DEFAULT_PATH)
|
||||
find_library(SDL3_LOCATION NAMES SDL3 SDL3-3.0 PATHS "${SDL3_ROOT_DIR}/lib" NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH)
|
||||
|
||||
get_filename_component(FULL_LIB_PATH "${SDL3_LOCATION}" REALPATH)
|
||||
ADD_IMPORTED_LIB(sdl3 "${SDL3_ROOT_DIR}/include" "${FULL_LIB_PATH}")
|
||||
|
||||
+4
-3
@@ -1,5 +1,5 @@
|
||||
|
||||
if(PLATFORM_WINDOWS OR PLATFORM_MAC OR CMAKE_CROSSCOMPILING)
|
||||
if(PLATFORM_WINDOWS OR PLATFORM_MAC OR CMAKE_CROSSCOMPILING OR ANDROID)
|
||||
add_library(freetype INTERFACE)
|
||||
|
||||
# We use prebuilt binaries for windows and mac
|
||||
@@ -10,7 +10,7 @@ if(PLATFORM_WINDOWS OR PLATFORM_MAC OR CMAKE_CROSSCOMPILING)
|
||||
|
||||
set(SEARCH_PATH "${FREETYPE_ROOT_DIR}/lib")
|
||||
|
||||
if(PLATFORM_WINDOWS OR CMAKE_CROSSCOMPILING)
|
||||
if((PLATFORM_WINDOWS OR CMAKE_CROSSCOMPILING) AND NOT ANDROID)
|
||||
set(LIBNAME "freetype*.dll")
|
||||
else()
|
||||
set(LIBNAME "libfreetype*")
|
||||
@@ -21,7 +21,8 @@ if(PLATFORM_WINDOWS OR PLATFORM_MAC OR CMAKE_CROSSCOMPILING)
|
||||
find_library(freetype_LOCATION
|
||||
NAMES freetype freetype281
|
||||
PATHS "${SEARCH_PATH}"
|
||||
NO_DEFAULT_PATH)
|
||||
NO_DEFAULT_PATH
|
||||
NO_CMAKE_FIND_ROOT_PATH)
|
||||
|
||||
if (${freetype_LOCATION} STREQUAL "freetype_LOCATION-NOTFOUND" AND CMAKE_CROSSCOMPILING)
|
||||
#Just try the default here and see if we can make it work.
|
||||
|
||||
+39
-11
@@ -3,6 +3,7 @@
|
||||
# without setting Qt6 up project wide
|
||||
|
||||
set(PREBUILT_VERSION_NAME "b561f6e8" CACHE INTERNAL "")
|
||||
set(ANDROID_PREBUILT_VERSION_NAME "android-prebuilts-v1" CACHE INTERNAL "")
|
||||
|
||||
set(FSO_PREBUILT_OVERRIDE "" CACHE PATH "Path to the prebuilt binaries, if empty the binaries will be downloaded.")
|
||||
set(PREBUILT_LIB_DIR "${CMAKE_CURRENT_BINARY_DIR}/prebuilt" CACHE INTERNAL "")
|
||||
@@ -14,15 +15,9 @@ function(get_prebuilt_path OUT_VAR)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if (IS_DIRECTORY "${PREBUILT_LIB_DIR}")
|
||||
if ("${DOWNLOADED_PREBUILT_VERSION}" STREQUAL "${PREBUILT_VERSION_NAME}")
|
||||
# Libraries already downloaded and up-to-date
|
||||
set(${OUT_VAR} "${PREBUILT_LIB_DIR}" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(PREBUILT_PATH)
|
||||
set(PREBUILT_CACHE_KEY "${PREBUILT_VERSION_NAME}")
|
||||
set(DOWNLOAD_HASH_ARGS)
|
||||
set(TAG_NAME "bin-${PREBUILT_VERSION_NAME}")
|
||||
if(PLATFORM_WINDOWS)
|
||||
if (IS_ARM64)
|
||||
@@ -34,6 +29,22 @@ function(get_prebuilt_path OUT_VAR)
|
||||
endif()
|
||||
elseif(PLATFORM_MAC)
|
||||
set(FILENAME "bin-mac.tar.gz")
|
||||
elseif(ANDROID)
|
||||
if(ANDROID_ABI STREQUAL "arm64-v8a")
|
||||
set(FILENAME "bin-android-arm64.tar.gz")
|
||||
set(PREBUILT_SHA256 "d56464fba097c16c6034ae483215045353078c0a339bb1566f0f7e20d063ca52")
|
||||
elseif(ANDROID_ABI STREQUAL "x86_64")
|
||||
set(FILENAME "bin-android-x64.tar.gz")
|
||||
set(PREBUILT_SHA256 "ab030d21200fb3ef799bb08d7a27ae973274a239dd244f51d68304e90a4021d5")
|
||||
elseif(ANDROID_ABI STREQUAL "armeabi-v7a")
|
||||
set(FILENAME "bin-android-arm32.tar.gz")
|
||||
set(PREBUILT_SHA256 "8c040685cb4e91cf1e95d106200aa8d852f4f32859e3d1fe6d8e995168b122b6")
|
||||
elseif(ANDROID_ABI STREQUAL "x86")
|
||||
set(FILENAME "bin-android-x86.tar.gz")
|
||||
set(PREBUILT_SHA256 "020bf41dc8c1d7414c12c050b2e3abc0a701795073ed48b18706b2a89c30e473")
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported Android ABI for prebuilt libraries: ${ANDROID_ABI}")
|
||||
endif()
|
||||
else()
|
||||
# Use Linux binaries...
|
||||
if (IS_ARM64)
|
||||
@@ -42,7 +53,23 @@ function(get_prebuilt_path OUT_VAR)
|
||||
set(FILENAME "bin-linux.tar.gz")
|
||||
endif()
|
||||
endif()
|
||||
set(DOWNLOAD_URL "https://github.com/scp-fs2open/scp-prebuilt/releases/download/${TAG_NAME}/${FILENAME}")
|
||||
if(ANDROID) # TODO: REMOVE THIS ONCE ANDROID PREBUILTS ARE IN THE SCP-PREBUILT REPO
|
||||
set(TAG_NAME "${ANDROID_PREBUILT_VERSION_NAME}")
|
||||
set(DOWNLOAD_URL "https://github.com/Shivansps/Fso-Android-Prebuilts/releases/download/${TAG_NAME}/${FILENAME}")
|
||||
set(DOWNLOAD_HASH_ARGS EXPECTED_HASH "SHA256=${PREBUILT_SHA256}")
|
||||
# Re-download when the Android release, ABI, or expected archive changes.
|
||||
set(PREBUILT_CACHE_KEY "${TAG_NAME}/${ANDROID_ABI}/${PREBUILT_SHA256}")
|
||||
else()
|
||||
set(DOWNLOAD_URL "https://github.com/scp-fs2open/scp-prebuilt/releases/download/${TAG_NAME}/${FILENAME}")
|
||||
endif()
|
||||
|
||||
if (IS_DIRECTORY "${PREBUILT_LIB_DIR}")
|
||||
if ("${DOWNLOADED_PREBUILT_VERSION}" STREQUAL "${PREBUILT_CACHE_KEY}")
|
||||
# Libraries already downloaded and up-to-date
|
||||
set(${OUT_VAR} "${PREBUILT_LIB_DIR}" PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
endif()
|
||||
set(DOWNLOAD_FILE "${CURRENT_ROOT}/${FILENAME}")
|
||||
|
||||
set(MAX_RETRIES 5)
|
||||
@@ -53,7 +80,8 @@ function(get_prebuilt_path OUT_VAR)
|
||||
endif()
|
||||
|
||||
message(STATUS "Downloading prebuilt libraries from \"${DOWNLOAD_URL}\" (try ${i}/${MAX_RETRIES})")
|
||||
file(DOWNLOAD "${DOWNLOAD_URL}" "${DOWNLOAD_FILE}" SHOW_PROGRESS TLS_VERIFY ON STATUS DOWNLOAD_STATUS_LIST)
|
||||
file(DOWNLOAD "${DOWNLOAD_URL}" "${DOWNLOAD_FILE}" SHOW_PROGRESS TLS_VERIFY ON
|
||||
${DOWNLOAD_HASH_ARGS} STATUS DOWNLOAD_STATUS_LIST)
|
||||
|
||||
list(GET DOWNLOAD_STATUS_LIST 0 DOWNLOAD_STATUS)
|
||||
list(GET DOWNLOAD_STATUS_LIST 1 DOWNLOAD_ERROR)
|
||||
@@ -95,6 +123,6 @@ function(get_prebuilt_path OUT_VAR)
|
||||
file(REMOVE "${DOWNLOAD_FILE}")
|
||||
|
||||
# We are done now. Set the cache variables and return the result
|
||||
set(DOWNLOADED_PREBUILT_VERSION "${PREBUILT_VERSION_NAME}" CACHE INTERNAL "")
|
||||
set(DOWNLOADED_PREBUILT_VERSION "${PREBUILT_CACHE_KEY}" CACHE INTERNAL "")
|
||||
set(${OUT_VAR} "${PREBUILT_LIB_DIR}" PARENT_SCOPE)
|
||||
endfunction(get_prebuilt_path)
|
||||
+2
-2
@@ -95,7 +95,7 @@ option(VULKAN_USE_PRECOMPILED "Force use of precompiled versions of Vulkan-Loade
|
||||
|
||||
set(USING_PREBUILT_VULKAN ${VULKAN_USE_PRECOMPILED})
|
||||
|
||||
if(PLATFORM_WINDOWS OR PLATFORM_MAC)
|
||||
if(PLATFORM_WINDOWS OR PLATFORM_MAC OR ANDROID)
|
||||
set(USING_PREBUILT_VULKAN TRUE)
|
||||
elseif(PLATFORM_LINUX AND FSO_BUILD_APPIMAGE)
|
||||
set(USING_PREBUILT_VULKAN TRUE)
|
||||
@@ -141,7 +141,7 @@ if(USING_PREBUILT_VULKAN)
|
||||
find_library(MOLTENVK_LIBRARY MoltenVK PATHS "${PREBUILT_PATH}" NO_DEFAULT_PATH)
|
||||
|
||||
add_target_copy_files("${MOLTENVK_LIBRARY}")
|
||||
else()
|
||||
elseif(NOT ANDROID) # Android ships its own Vulkan loader (system libvulkan.so)
|
||||
message(STATUS "Using pre-built Vulkan-Loader library.")
|
||||
|
||||
if(PLATFORM_WINDOWS)
|
||||
|
||||
Reference in new issue
Block a user