mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-01-24 16:06:51 +08:00
android polishing and bug fixes
This commit is contained in:
parent
0e15df6f59
commit
2a5e9768db
88
.efrocachemap
generated
88
.efrocachemap
generated
@ -4060,50 +4060,50 @@
|
||||
"build/assets/windows/Win32/ucrtbased.dll": "2def5335207d41b21b9823f6805997f1",
|
||||
"build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae",
|
||||
"build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599",
|
||||
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "c2b80379179c8731be37581e52259377",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "0a2257e46a20ae6453d888515a00f1a8",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "a8bf3602e161931f96c41989d9d4e630",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "15e5d036605366840182cf3f86d247ae",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "4212f5014bcf30f5ceb6e9ed00c1b443",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "a9ee1b8f07dc7466b4daf90a34991f3b",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "cbc9bf3f8ddce331912b6dbd8f1c6415",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "6b4458aa454391fa0070099ef4cac711",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "fae27ee9108877bd75aaa222f02f239d",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "37f2b4219ffe85aa1d28ab7df7fd4c44",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "3f8de90abf2069a0881f8d91f1ec78b2",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "62a6ee7583ef9d83f9bb1601fce6ddaa",
|
||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "f495a8547ca8b824f80006603537d1cf",
|
||||
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "672c351fca7843d85a2be7aba13faf1f",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "50f6d105ad4c2bb2df4f3d335b6d2cfa",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "44e1d633accc2410ca6d825e4c464f45",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "5cce0ce595304313ac28c02e235f19d7",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "9433fc9da2f4b7255f2c7fa95868604a",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "18eb1871a6db9bbe17f5e9678d3d492a",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "cf51dafe0553d06a44349d0911c21d71",
|
||||
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "c22901e06e88a55cce0b4e08bbf41a4c",
|
||||
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "a27963487e346338e4c216bd4fbb9e2a",
|
||||
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "c22901e06e88a55cce0b4e08bbf41a4c",
|
||||
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "a27963487e346338e4c216bd4fbb9e2a",
|
||||
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "2663c888aec894656bd8c49932bd7729",
|
||||
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "5e57d12a3cfcfbc47b0293c3cb9fdca9",
|
||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "2663c888aec894656bd8c49932bd7729",
|
||||
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "5e57d12a3cfcfbc47b0293c3cb9fdca9",
|
||||
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "0f7dbe6fb3e28a51904aa822b509da0f",
|
||||
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "081b766945b52460a4f1afc01faa0652",
|
||||
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "0f7dbe6fb3e28a51904aa822b509da0f",
|
||||
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "081b766945b52460a4f1afc01faa0652",
|
||||
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "ad609c63f68417d5211bbfb23ce4affe",
|
||||
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "852fe46c736082611a831a618923c241",
|
||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "36fbda7829ed5c2862c34feb09b03402",
|
||||
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "852fe46c736082611a831a618923c241",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "8ec03141da397548f8a06259222c14e3",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "9f20a365cc0ec1831e0e301a34198b0e",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "37980dc34b9ad281cdab738f7053af26",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "148e0f3a1ee77607d0b93a636b253295",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "e20a2aa49741757075634f422dc5ac7a",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "fa7018f7c2ee8c952bff7879b92709cb",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "cd38a41872e74b43de005375330e3cd0",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "5bc4faf70f09f56e2ced27cafe2ad3e6",
|
||||
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "84b6be9d4a7d8a544993006751c7f632",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "f87ec55a2f3732de69bd7ca56366ac5e",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "cc88073fd308bca9e681fa585cc8f534",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "a671348d891eb53c6882047f270c46ed",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "667d5118d18ec5e9e822224868d24380",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "f49b0ac8dc49ef622c2da81de1134425",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "b38fb26bbcc45d9ac3990a4b4b2cec0a",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "3100977be9a4cb9271d1ee60d00afd87",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "1bd4a1960660c6f1b7c355de6d3d8658",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "3674cde02fbe0059d3f7281344970d16",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "384f40a3d4652323c218f1dfa650ec17",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "cdf0902eeacf4667ddae3b489fb9dd38",
|
||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "283aaa1498e719442445c6e24297d5f9",
|
||||
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "7b0facb90fc8b2918cb124c0dcd360af",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "981d0be875bb37e94840040ff41cf52e",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "f850af922341d32d237c249daef4383c",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "d3a3e7fb95fc7fbe586646134060d4e2",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "f1a9d399fa0313ee35797eec3b6a465a",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "031e6fd40465f5b8d9f6390efcb7e82d",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "4c8f3095b6a700d1c9a73aabaee98e63",
|
||||
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "add3863bc3c332a1196db0673fde5587",
|
||||
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "e3c41c240bb333fc53240f64d6e9583e",
|
||||
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "add3863bc3c332a1196db0673fde5587",
|
||||
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "e3c41c240bb333fc53240f64d6e9583e",
|
||||
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "0b0ec8be8c575beba2935fdc9aa03ce5",
|
||||
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "04c21c4226944f71230420f9b399d1e4",
|
||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "0b0ec8be8c575beba2935fdc9aa03ce5",
|
||||
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "04c21c4226944f71230420f9b399d1e4",
|
||||
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "e80ffa41dcc78dbd75baf89fadb096b4",
|
||||
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "6514628d8ce1c046726de69fa0086613",
|
||||
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "e80ffa41dcc78dbd75baf89fadb096b4",
|
||||
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "6514628d8ce1c046726de69fa0086613",
|
||||
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "9f4f5c5043d66fa3bdeb16b0599b5de4",
|
||||
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "ddcb6a381ef5eaef6b1650cd94c498fc",
|
||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "a3124c863c4b80de5acc20a7c9d49492",
|
||||
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "ddcb6a381ef5eaef6b1650cd94c498fc",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "9eba00c4b0b0e11a16d8396f26d34868",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "fb9041aadad56623332d3fa97684784e",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "34c36a14ff8a8d7661f923cc8f1149c9",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "e87159408ee82988c641c963cb53c4e3",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "569e1aac13b5bb8abd7df8e91ecc7554",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "d85d2e4cbcaf1c5fe2a3c984d35a4ed8",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "5a33774c2533bfd695facb4a68767bb8",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "87890c51eb8a6eab5e1847bf19e1c1ba",
|
||||
"src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c",
|
||||
"src/assets/ba_data/python/babase/_mgen/enums.py": "28323912b56ec07701eda3d41a6a4101",
|
||||
"src/ballistica/base/mgen/pyembed/binding_base.inc": "72bfed2cce8ff19741989dec28302f3f",
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
### 1.7.30 (build 21639, api 8, 2023-11-30)
|
||||
### 1.7.30 (build 21652, api 8, 2023-12-01)
|
||||
- Continued work on the big 1.7.28 update.
|
||||
- Got the Android version back up and running. There's been lots of cleanup and
|
||||
simplification on the Android layer, cleaning out years of cruft. This should
|
||||
|
||||
@ -229,6 +229,15 @@ class App:
|
||||
self.lang = LanguageSubsystem()
|
||||
self.plugins = PluginSubsystem()
|
||||
|
||||
@property
|
||||
def active(self) -> bool:
|
||||
"""Whether the app is currently front and center.
|
||||
|
||||
This will be False when the app is hidden, other activities
|
||||
are covering it, etc. (depending on the platform).
|
||||
"""
|
||||
return _babase.app_is_active()
|
||||
|
||||
@property
|
||||
def aioloop(self) -> asyncio.AbstractEventLoop:
|
||||
"""The logic thread's asyncio event loop.
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import babase
|
||||
@ -31,6 +33,7 @@ class AdsSubsystem:
|
||||
self.last_in_game_ad_remove_message_show_time: float | None = None
|
||||
self.last_ad_completion_time: float | None = None
|
||||
self.last_ad_was_short = False
|
||||
self._fallback_task: asyncio.Task | None = None
|
||||
|
||||
def do_remove_in_game_ads_message(self) -> None:
|
||||
"""(internal)"""
|
||||
@ -181,36 +184,53 @@ class AdsSubsystem:
|
||||
|
||||
# If we're *still* cleared to show, actually tell the system to show.
|
||||
if show:
|
||||
# As a safety-check, set up an object that will run
|
||||
# the completion callback if we've returned and sat for 10 seconds
|
||||
# (in case some random ad network doesn't properly deliver its
|
||||
# completion callback).
|
||||
# As a safety-check, we set up an object that will run the
|
||||
# completion callback if we've returned and sat for several
|
||||
# seconds (in case some random ad network doesn't properly
|
||||
# deliver its completion callback).
|
||||
class _Payload:
|
||||
def __init__(self, pcall: Callable[[], Any]):
|
||||
self._call = pcall
|
||||
self._ran = False
|
||||
|
||||
def run(self, fallback: bool = False) -> None:
|
||||
"""Run fallback call (and issue a warning about it)."""
|
||||
"""Run the payload."""
|
||||
assert app.classic is not None
|
||||
if not self._ran:
|
||||
if fallback:
|
||||
lanst = app.classic.ads.last_ad_network_set_time
|
||||
print(
|
||||
'ERROR: relying on fallback ad-callback! '
|
||||
'last network: '
|
||||
+ app.classic.ads.last_ad_network
|
||||
+ ' (set '
|
||||
+ str(int(time.time() - lanst))
|
||||
+ 's ago); purpose='
|
||||
+ app.classic.ads.last_ad_purpose
|
||||
logging.error(
|
||||
'Relying on fallback ad-callback! '
|
||||
'last network: %s (set %s seconds ago);'
|
||||
' purpose=%s.',
|
||||
app.classic.ads.last_ad_network,
|
||||
time.time() - lanst,
|
||||
app.classic.ads.last_ad_purpose,
|
||||
)
|
||||
babase.pushcall(self._call)
|
||||
self._ran = True
|
||||
|
||||
payload = _Payload(call)
|
||||
|
||||
# Set up our backup.
|
||||
with babase.ContextRef.empty():
|
||||
babase.apptimer(5.0, lambda: payload.run(fallback=True))
|
||||
# Note to self: Previously this was a simple 5 second
|
||||
# timer because the app got totally suspended while ads
|
||||
# were showing (which delayed the timer), but these days
|
||||
# the app may continue to run, so we need to be more
|
||||
# careful and only fire the fallback after we see that
|
||||
# the app has been front-and-center for several seconds.
|
||||
async def add_fallback_task() -> None:
|
||||
activesecs = 5
|
||||
while activesecs > 0:
|
||||
if babase.app.active:
|
||||
activesecs -= 1
|
||||
await asyncio.sleep(1.0)
|
||||
payload.run(fallback=True)
|
||||
|
||||
_fallback_task = babase.app.aioloop.create_task(
|
||||
add_fallback_task()
|
||||
)
|
||||
self.show_ad('between_game', on_completion_call=payload.run)
|
||||
else:
|
||||
babase.pushcall(call) # Just run the callback without the ad.
|
||||
|
||||
@ -52,7 +52,7 @@ if TYPE_CHECKING:
|
||||
|
||||
# Build number and version of the ballistica binary we expect to be
|
||||
# using.
|
||||
TARGET_BALLISTICA_BUILD = 21639
|
||||
TARGET_BALLISTICA_BUILD = 21652
|
||||
TARGET_BALLISTICA_VERSION = '1.7.30'
|
||||
|
||||
|
||||
|
||||
@ -551,9 +551,11 @@ def show_offer() -> bool:
|
||||
if bui.native_review_request_supported():
|
||||
bui.native_review_request()
|
||||
else:
|
||||
feedback.ask_for_rating()
|
||||
if app.ui_v1.available:
|
||||
feedback.ask_for_rating()
|
||||
else:
|
||||
SpecialOfferWindow(app.classic.special_offer)
|
||||
if app.ui_v1.available:
|
||||
SpecialOfferWindow(app.classic.special_offer)
|
||||
|
||||
app.classic.special_offer = None
|
||||
return True
|
||||
|
||||
@ -25,138 +25,13 @@ void AppAdapter::OnMainThreadStartApp() {
|
||||
}
|
||||
|
||||
void AppAdapter::OnAppStart() { assert(g_base->InLogicThread()); }
|
||||
void AppAdapter::OnAppPause() { assert(g_base->InLogicThread()); }
|
||||
void AppAdapter::OnAppResume() { assert(g_base->InLogicThread()); }
|
||||
void AppAdapter::OnAppSuspend() { assert(g_base->InLogicThread()); }
|
||||
void AppAdapter::OnAppUnsuspend() { assert(g_base->InLogicThread()); }
|
||||
void AppAdapter::OnAppShutdown() { assert(g_base->InLogicThread()); }
|
||||
void AppAdapter::OnAppShutdownComplete() { assert(g_base->InLogicThread()); }
|
||||
void AppAdapter::OnScreenSizeChange() { assert(g_base->InLogicThread()); }
|
||||
void AppAdapter::DoApplyAppConfig() { assert(g_base->InLogicThread()); }
|
||||
|
||||
void AppAdapter::OnAppSuspend_() {
|
||||
assert(g_core->InMainThread());
|
||||
|
||||
// IMPORTANT: Any pause related stuff that event-loop-threads need to do
|
||||
// should be done from their registered pause-callbacks. If we instead
|
||||
// push runnables to them from here they may or may not be called before
|
||||
// their event-loop is actually paused.
|
||||
|
||||
// Pause all event loops.
|
||||
EventLoop::SetEventLoopsSuspended(true);
|
||||
|
||||
if (g_base->network_reader) {
|
||||
g_base->network_reader->OnAppPause();
|
||||
}
|
||||
g_base->networking->OnAppPause();
|
||||
}
|
||||
|
||||
void AppAdapter::OnAppUnsuspend_() {
|
||||
assert(g_core->InMainThread());
|
||||
|
||||
// Spin all event-loops back up.
|
||||
EventLoop::SetEventLoopsSuspended(false);
|
||||
|
||||
// Run resumes that expect to happen in the main thread.
|
||||
g_base->network_reader->OnAppResume();
|
||||
g_base->networking->OnAppResume();
|
||||
|
||||
// When resuming from a suspended state, we may want to pause whatever
|
||||
// game was running when we last were active.
|
||||
//
|
||||
// TODO(efro): we should make this smarter so it doesn't happen if we're
|
||||
// in a network game or something that we can't pause; bringing up the
|
||||
// menu doesn't really accomplish anything there.
|
||||
//
|
||||
// In general this probably should be handled at a higher level.
|
||||
// if (g_core->should_pause_active_game) {
|
||||
// g_core->should_pause_active_game = false;
|
||||
|
||||
// // If we've been completely backgrounded, send a menu-press command to
|
||||
// // the game; this will bring up a pause menu if we're in the game/etc.
|
||||
// if (!g_base->ui->MainMenuVisible()) {
|
||||
// g_base->ui->PushMainMenuPressCall(nullptr);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
void AppAdapter::SuspendApp() {
|
||||
assert(g_core);
|
||||
assert(g_core->InMainThread());
|
||||
|
||||
if (app_suspended_) {
|
||||
Log(LogLevel::kWarning,
|
||||
"AppAdapter::SuspendApp() called with app already suspended.");
|
||||
return;
|
||||
}
|
||||
|
||||
millisecs_t start_time{core::CorePlatform::GetCurrentMillisecs()};
|
||||
|
||||
// Apple mentioned 5 seconds to run stuff once backgrounded or they bring
|
||||
// down the hammer. Let's aim to stay under 2.
|
||||
millisecs_t max_duration{2000};
|
||||
|
||||
g_core->platform->DebugLog(
|
||||
"SuspendApp@"
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs()));
|
||||
app_suspended_ = true;
|
||||
OnAppSuspend_();
|
||||
|
||||
// We assume that the OS will completely suspend our process the moment we
|
||||
// return from this call (though this is not technically true on all
|
||||
// platforms). So we want to spin and wait for threads to actually process
|
||||
// the pause message.
|
||||
size_t running_thread_count{};
|
||||
while (std::abs(core::CorePlatform::GetCurrentMillisecs() - start_time)
|
||||
< max_duration) {
|
||||
// If/when we get to a point with no threads waiting to be paused, we're
|
||||
// good to go.
|
||||
auto threads{EventLoop::GetStillSuspendingEventLoops()};
|
||||
running_thread_count = threads.size();
|
||||
if (running_thread_count == 0) {
|
||||
if (g_buildconfig.debug_build()) {
|
||||
Log(LogLevel::kDebug,
|
||||
"SuspendApp() completed in "
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs()
|
||||
- start_time)
|
||||
+ "ms.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If we made it here, we timed out. Complain.
|
||||
Log(LogLevel::kError,
|
||||
std::string("SuspendApp() took too long; ")
|
||||
+ std::to_string(running_thread_count)
|
||||
+ " threads not yet paused after "
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs()
|
||||
- start_time)
|
||||
+ " ms.");
|
||||
}
|
||||
|
||||
void AppAdapter::UnsuspendApp() {
|
||||
assert(g_core);
|
||||
assert(g_core->InMainThread());
|
||||
|
||||
if (!app_suspended_) {
|
||||
Log(LogLevel::kWarning,
|
||||
"AppAdapter::UnsuspendApp() called with app not in suspendedstate.");
|
||||
return;
|
||||
}
|
||||
millisecs_t start_time{core::CorePlatform::GetCurrentMillisecs()};
|
||||
g_core->platform->DebugLog(
|
||||
"UnsuspendApp@"
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs()));
|
||||
app_suspended_ = false;
|
||||
OnAppUnsuspend_();
|
||||
if (g_buildconfig.debug_build()) {
|
||||
Log(LogLevel::kDebug,
|
||||
"UnsuspendApp() completed in "
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs()
|
||||
- start_time)
|
||||
+ "ms.");
|
||||
}
|
||||
}
|
||||
|
||||
void AppAdapter::RunMainThreadEventLoopToCompletion() {
|
||||
FatalError("RunMainThreadEventLoopToCompletion is not implemented here.");
|
||||
}
|
||||
@ -242,41 +117,6 @@ auto AppAdapter::GetGraphicsClientContext() -> GraphicsClientContext* {
|
||||
auto AppAdapter::GetKeyRepeatDelay() -> float { return 0.3f; }
|
||||
auto AppAdapter::GetKeyRepeatInterval() -> float { return 0.08f; }
|
||||
|
||||
auto AppAdapter::ClipboardIsSupported() -> bool {
|
||||
// We only call our actual virtual function once.
|
||||
if (!have_clipboard_is_supported_) {
|
||||
clipboard_is_supported_ = DoClipboardIsSupported();
|
||||
have_clipboard_is_supported_ = true;
|
||||
}
|
||||
return clipboard_is_supported_;
|
||||
}
|
||||
|
||||
auto AppAdapter::ClipboardHasText() -> bool {
|
||||
// If subplatform says they don't support clipboards, don't even ask.
|
||||
if (!ClipboardIsSupported()) {
|
||||
return false;
|
||||
}
|
||||
return DoClipboardHasText();
|
||||
}
|
||||
|
||||
void AppAdapter::ClipboardSetText(const std::string& text) {
|
||||
// If subplatform says they don't support clipboards, this is an error.
|
||||
if (!ClipboardIsSupported()) {
|
||||
throw Exception("ClipboardSetText called with no clipboard support.",
|
||||
PyExcType::kRuntime);
|
||||
}
|
||||
DoClipboardSetText(text);
|
||||
}
|
||||
|
||||
auto AppAdapter::ClipboardGetText() -> std::string {
|
||||
// If subplatform says they don't support clipboards, this is an error.
|
||||
if (!ClipboardIsSupported()) {
|
||||
throw Exception("ClipboardGetText called with no clipboard support.",
|
||||
PyExcType::kRuntime);
|
||||
}
|
||||
return DoClipboardGetText();
|
||||
}
|
||||
|
||||
auto AppAdapter::DoClipboardIsSupported() -> bool { return false; }
|
||||
|
||||
auto AppAdapter::DoClipboardHasText() -> bool {
|
||||
@ -311,4 +151,8 @@ void AppAdapter::NativeReviewRequest() {
|
||||
|
||||
void AppAdapter::DoNativeReviewRequest() { FatalError("Fixme unimplemented."); }
|
||||
|
||||
auto AppAdapter::ShouldSilenceAudioWhenInactive() -> bool const {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
@ -22,8 +22,8 @@ class AppAdapter {
|
||||
|
||||
// Logic thread callbacks.
|
||||
virtual void OnAppStart();
|
||||
virtual void OnAppPause();
|
||||
virtual void OnAppResume();
|
||||
virtual void OnAppSuspend();
|
||||
virtual void OnAppUnsuspend();
|
||||
virtual void OnAppShutdown();
|
||||
virtual void OnAppShutdownComplete();
|
||||
virtual void OnScreenSizeChange();
|
||||
@ -88,9 +88,9 @@ class AppAdapter {
|
||||
/// plugged in or unplugged/etc. Default implementation returns true.
|
||||
virtual auto ShouldUseCursor() -> bool;
|
||||
|
||||
/// Return whether the app-adapter is having the OS show a cursor.
|
||||
/// If this returns false, the engine will take care of drawing a cursor
|
||||
/// when necessary. If true, SetHardwareCursorVisible will be called
|
||||
/// Return whether the app-adapter is having the OS show a cursor. If this
|
||||
/// returns false, the engine will take care of drawing a cursor when
|
||||
/// necessary. If true, SetHardwareCursorVisible will be called
|
||||
/// periodically to inform the adapter what the cursor state should be.
|
||||
/// The default implementation returns false;
|
||||
virtual auto HasHardwareCursor() -> bool;
|
||||
@ -106,21 +106,6 @@ class AppAdapter {
|
||||
/// values.
|
||||
virtual void CursorPositionForDraw(float* x, float* y);
|
||||
|
||||
/// Put the app into a suspended state. Should be called from the main
|
||||
/// thread. Pauses work, closes network sockets, etc. May correspond to
|
||||
/// being backgrounded on mobile, being minimized on desktop, etc. It is
|
||||
/// assumed that, as soon as this call returns, all work is finished and
|
||||
/// all threads can be suspended by the OS without any negative side
|
||||
/// effects.
|
||||
void SuspendApp();
|
||||
|
||||
/// Return the app to a running state from a suspended one. Can correspond
|
||||
/// to foregrounding on mobile, unminimizing on desktop, etc. Spins
|
||||
/// threads back up, re-opens network sockets, etc.
|
||||
void UnsuspendApp();
|
||||
|
||||
auto app_suspended() const { return app_suspended_; }
|
||||
|
||||
/// Return whether this AppAdapter supports a 'fullscreen' toggle for its
|
||||
/// display. This will affect whether that option is available in display
|
||||
/// settings or via a hotkey. Must be called from the logic thread.
|
||||
@ -150,6 +135,12 @@ class AppAdapter {
|
||||
/// Return whether this AppAdapter supports max-fps controls for its display.
|
||||
virtual auto SupportsMaxFPS() -> bool const;
|
||||
|
||||
/// Return whether audio should be silenced when the app is inactive.
|
||||
/// On Desktop systems it is generally normal to continue to hear things
|
||||
/// even if their windows are hidden, but on mobile we probably want to
|
||||
/// silence our audio when phone calls, ads, etc. pop up over it.
|
||||
virtual auto ShouldSilenceAudioWhenInactive() -> bool const;
|
||||
|
||||
/// Return whether this platform supports soft-quit. A soft quit is
|
||||
/// when the app is reset/backgrounded/etc. but remains running in case
|
||||
/// needed again. Generally this is the behavior on mobile apps.
|
||||
@ -206,22 +197,6 @@ class AppAdapter {
|
||||
virtual auto GetKeyRepeatDelay() -> float;
|
||||
virtual auto GetKeyRepeatInterval() -> float;
|
||||
|
||||
/// Return whether clipboard operations are supported at all. This gets
|
||||
/// called when determining whether to display clipboard related UI
|
||||
/// elements/etc.
|
||||
auto ClipboardIsSupported() -> bool;
|
||||
|
||||
/// Return whether there is currently text on the clipboard.
|
||||
auto ClipboardHasText() -> bool;
|
||||
|
||||
/// Set current clipboard text. Raises an Exception if clipboard is
|
||||
/// unsupported.
|
||||
void ClipboardSetText(const std::string& text);
|
||||
|
||||
/// Return current text from the clipboard. Raises an Exception if
|
||||
/// clipboard is unsupported or if there's no text on the clipboard.
|
||||
auto ClipboardGetText() -> std::string;
|
||||
|
||||
/// Push a raw pointer Runnable to the platform's 'main' thread. The main
|
||||
/// thread should call its RunAndLogErrors() method and then delete it.
|
||||
virtual void DoPushMainThreadRunnable(Runnable* runnable) = 0;
|
||||
@ -239,25 +214,19 @@ class AppAdapter {
|
||||
/// Asynchronously kick off a native review request.
|
||||
void NativeReviewRequest();
|
||||
|
||||
protected:
|
||||
virtual ~AppAdapter();
|
||||
|
||||
virtual auto DoClipboardIsSupported() -> bool;
|
||||
virtual auto DoClipboardHasText() -> bool;
|
||||
virtual void DoClipboardSetText(const std::string& text);
|
||||
virtual auto DoClipboardGetText() -> std::string;
|
||||
|
||||
protected:
|
||||
virtual ~AppAdapter();
|
||||
|
||||
/// Override to implement native review requests. Will be called in the
|
||||
/// main thread.
|
||||
virtual void DoNativeReviewRequest();
|
||||
|
||||
private:
|
||||
void OnAppSuspend_();
|
||||
void OnAppUnsuspend_();
|
||||
|
||||
bool app_suspended_{};
|
||||
bool have_clipboard_is_supported_{};
|
||||
bool clipboard_is_supported_{};
|
||||
};
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
@ -66,7 +66,6 @@ class AppAdapterApple : public AppAdapter {
|
||||
private:
|
||||
class ScopedAllowGraphics_;
|
||||
|
||||
// void UpdateScreenSizes_();
|
||||
void ReloadRenderer_(const GraphicsSettings* settings);
|
||||
|
||||
std::thread::id graphics_thread_{};
|
||||
|
||||
@ -471,6 +471,9 @@ void AppAdapterSDL::HandleSDLEvent_(const SDL_Event& event) {
|
||||
// it to the config so that UIs can poll for it and pick up the
|
||||
// change. We don't do this on other platforms where a maximized
|
||||
// window is more distinctly different than a fullscreen one.
|
||||
// Though I guess some Linux window managers have a fullscreen
|
||||
// function so theoretically we should there. Le sigh. Maybe SDL
|
||||
// 3 will tidy up this situation.
|
||||
fullscreen_ = true;
|
||||
g_base->logic->event_loop()->PushCall([] {
|
||||
g_base->python->objs()
|
||||
@ -497,18 +500,22 @@ void AppAdapterSDL::HandleSDLEvent_(const SDL_Event& event) {
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_HIDDEN: {
|
||||
// Let's keep track of when we're hidden so we can stop drawing
|
||||
// and sleep more. Theoretically we could put the app into a full
|
||||
// suspended state like we do on mobile (pausing event loops/etc.)
|
||||
// but that would be more involved; we'd need to ignore most SDL
|
||||
// events while sleeping (except for SDL_WINDOWEVENT_SHOWN) and
|
||||
// would need to rebuild our controller lists/etc when we resume.
|
||||
// For now just gonna keep things simple and keep running.
|
||||
// We plug this into the app's overall 'Active' state so it can
|
||||
// pause stuff or throttle down processing or whatever else.
|
||||
if (!hidden_) {
|
||||
g_base->SetAppActive(false);
|
||||
}
|
||||
// Also note that we are *completely* hidden, so we can totally
|
||||
// stop drawing ('Inactive' app state does not imply this in and
|
||||
// of itself).
|
||||
hidden_ = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_WINDOWEVENT_SHOWN: {
|
||||
if (hidden_) {
|
||||
g_base->SetAppActive(true);
|
||||
}
|
||||
hidden_ = false;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -14,8 +14,8 @@ void AppMode::OnActivate() {}
|
||||
void AppMode::OnDeactivate() {}
|
||||
|
||||
void AppMode::OnAppStart() {}
|
||||
void AppMode::OnAppPause() {}
|
||||
void AppMode::OnAppResume() {}
|
||||
void AppMode::OnAppSuspend() {}
|
||||
void AppMode::OnAppUnsuspend() {}
|
||||
void AppMode::OnAppShutdown() {}
|
||||
void AppMode::OnAppShutdownComplete() {}
|
||||
|
||||
|
||||
@ -26,8 +26,8 @@ class AppMode {
|
||||
|
||||
/// Logic thread callbacks that run while the app-mode is active.
|
||||
virtual void OnAppStart();
|
||||
virtual void OnAppPause();
|
||||
virtual void OnAppResume();
|
||||
virtual void OnAppSuspend();
|
||||
virtual void OnAppUnsuspend();
|
||||
virtual void OnAppShutdown();
|
||||
virtual void OnAppShutdownComplete();
|
||||
virtual void DoApplyAppConfig();
|
||||
|
||||
@ -33,9 +33,9 @@ void Audio::Reset() {
|
||||
|
||||
void Audio::OnAppStart() { assert(g_base->InLogicThread()); }
|
||||
|
||||
void Audio::OnAppPause() { assert(g_base->InLogicThread()); }
|
||||
void Audio::OnAppSuspend() { assert(g_base->InLogicThread()); }
|
||||
|
||||
void Audio::OnAppResume() { assert(g_base->InLogicThread()); }
|
||||
void Audio::OnAppUnsuspend() { assert(g_base->InLogicThread()); }
|
||||
|
||||
void Audio::OnAppShutdown() { assert(g_base->InLogicThread()); }
|
||||
|
||||
|
||||
@ -21,8 +21,8 @@ class Audio {
|
||||
void Reset();
|
||||
|
||||
virtual void OnAppStart();
|
||||
virtual void OnAppPause();
|
||||
virtual void OnAppResume();
|
||||
virtual void OnAppSuspend();
|
||||
virtual void OnAppUnsuspend();
|
||||
virtual void OnAppShutdown();
|
||||
virtual void OnAppShutdownComplete();
|
||||
virtual void DoApplyAppConfig();
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "ballistica/base/app_adapter/app_adapter.h"
|
||||
#include "ballistica/base/assets/assets.h"
|
||||
#include "ballistica/base/assets/sound_asset.h"
|
||||
#include "ballistica/base/audio/al_sys.h"
|
||||
@ -111,7 +112,7 @@ class AudioServer::ThreadSource_ : public Object {
|
||||
std::unique_ptr<AudioSource> client_source_;
|
||||
float fade_{1.0f};
|
||||
float gain_{1.0f};
|
||||
AudioServer* audio_thread_{};
|
||||
AudioServer* audio_server_{};
|
||||
bool valid_{};
|
||||
const Object::Ref<SoundAsset>* source_sound_{};
|
||||
int id_{};
|
||||
@ -343,13 +344,13 @@ void AudioServer::SetSuspended_(bool suspend) {
|
||||
try {
|
||||
alcDevicePauseSOFT(device);
|
||||
} catch (const std::exception& e) {
|
||||
g_core->platform->DebugLog(
|
||||
g_core->platform->LowLevelDebugLog(
|
||||
std::string("EXC pausing alcDevice: ")
|
||||
+ g_core->platform->DemangleCXXSymbol(typeid(e).name()) + " "
|
||||
+ e.what());
|
||||
throw;
|
||||
} catch (...) {
|
||||
g_core->platform->DebugLog("UNKNOWN EXC pausing alcDevice");
|
||||
g_core->platform->LowLevelDebugLog("UNKNOWN EXC pausing alcDevice");
|
||||
throw;
|
||||
}
|
||||
#endif
|
||||
@ -381,13 +382,13 @@ void AudioServer::SetSuspended_(bool suspend) {
|
||||
try {
|
||||
alcDeviceResumeSOFT(device);
|
||||
} catch (const std::exception& e) {
|
||||
g_core->platform->DebugLog(
|
||||
g_core->platform->LowLevelDebugLog(
|
||||
std::string("EXC resuming alcDevice: ")
|
||||
+ g_core->platform->DemangleCXXSymbol(typeid(e).name()) + " "
|
||||
+ e.what());
|
||||
throw;
|
||||
} catch (...) {
|
||||
g_core->platform->DebugLog("UNKNOWN EXC resuming alcDevice");
|
||||
g_core->platform->LowLevelDebugLog("UNKNOWN EXC resuming alcDevice");
|
||||
throw;
|
||||
}
|
||||
#endif
|
||||
@ -689,7 +690,7 @@ void AudioServer::Process_() {
|
||||
assert(g_base->InAudioThread());
|
||||
millisecs_t real_time = g_core->GetAppTimeMillisecs();
|
||||
|
||||
// If we're suspended we don't do nothin'.
|
||||
// Only do real work if we're in normal running mode.
|
||||
if (!suspended_ && !shutting_down_) {
|
||||
// Do some loading...
|
||||
have_pending_loads_ = g_base->assets->RunPendingAudioLoads();
|
||||
@ -711,6 +712,21 @@ void AudioServer::Process_() {
|
||||
}
|
||||
}
|
||||
|
||||
// If the app has switched active/inactive state, update
|
||||
// our volumes (we may silence our audio in these cases).
|
||||
auto app_active = g_base->app_active();
|
||||
if (app_active != app_active_) {
|
||||
app_active_ = app_active;
|
||||
if (g_base->app_adapter->ShouldSilenceAudioWhenInactive()) {
|
||||
app_active_volume_ = app_active ? 1.0f : 0.0f;
|
||||
} else {
|
||||
app_active_volume_ = 1.0f;
|
||||
}
|
||||
for (auto&& i : sources_) {
|
||||
i->UpdateVolume();
|
||||
}
|
||||
}
|
||||
|
||||
#if BA_ENABLE_AUDIO
|
||||
CHECK_AL_ERROR;
|
||||
#endif
|
||||
@ -792,9 +808,9 @@ void AudioServer::FadeSoundOut(uint32_t play_id, uint32_t time) {
|
||||
// delete c;
|
||||
// }
|
||||
|
||||
AudioServer::ThreadSource_::ThreadSource_(AudioServer* audio_thread_in,
|
||||
AudioServer::ThreadSource_::ThreadSource_(AudioServer* audio_server_in,
|
||||
int id_in, bool* valid_out)
|
||||
: id_(id_in), audio_thread_(audio_thread_in) {
|
||||
: id_(id_in), audio_server_(audio_server_in) {
|
||||
#if BA_ENABLE_AUDIO
|
||||
assert(g_core);
|
||||
assert(valid_out != nullptr);
|
||||
@ -839,10 +855,10 @@ AudioServer::ThreadSource_::~ThreadSource_() {
|
||||
Stop();
|
||||
|
||||
// Remove us from sources list.
|
||||
for (auto i = audio_thread_->sources_.begin();
|
||||
i != audio_thread_->sources_.end(); ++i) {
|
||||
for (auto i = audio_server_->sources_.begin();
|
||||
i != audio_server_->sources_.end(); ++i) {
|
||||
if (*i == this) {
|
||||
audio_thread_->sources_.erase(i);
|
||||
audio_server_->sources_.erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1079,12 +1095,12 @@ void AudioServer::ThreadSource_::ExecPlay() {
|
||||
looping_ = false;
|
||||
|
||||
// Push us on the list of streaming sources if we're not on it.
|
||||
for (auto&& i : audio_thread_->streaming_sources_) {
|
||||
for (auto&& i : audio_server_->streaming_sources_) {
|
||||
if (i == this) {
|
||||
throw Exception();
|
||||
}
|
||||
}
|
||||
audio_thread_->streaming_sources_.push_back(this);
|
||||
audio_server_->streaming_sources_.push_back(this);
|
||||
|
||||
// Make sure stereo sounds aren't positional.
|
||||
// This is default behavior on Mac/Win, but we enforce it for linux.
|
||||
@ -1162,10 +1178,10 @@ void AudioServer::ThreadSource_::ExecStop() {
|
||||
if (streamer_.Exists()) {
|
||||
assert(is_streamed_);
|
||||
streamer_->Stop();
|
||||
for (auto i = audio_thread_->streaming_sources_.begin();
|
||||
i != audio_thread_->streaming_sources_.end(); ++i) {
|
||||
for (auto i = audio_server_->streaming_sources_.begin();
|
||||
i != audio_server_->streaming_sources_.end(); ++i) {
|
||||
if (*i == this) {
|
||||
audio_thread_->streaming_sources_.erase(i);
|
||||
audio_server_->streaming_sources_.erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1182,15 +1198,16 @@ void AudioServer::ThreadSource_::ExecStop() {
|
||||
void AudioServer::ThreadSource_::UpdateVolume() {
|
||||
#if BA_ENABLE_AUDIO
|
||||
assert(g_base->InAudioThread());
|
||||
if (g_base->audio_server->suspended_
|
||||
|| g_base->audio_server->shutting_down_) {
|
||||
if (audio_server_->suspended_ || audio_server_->shutting_down_) {
|
||||
return;
|
||||
}
|
||||
float val = gain_ * fade_;
|
||||
val *= audio_server_->app_active_volume_;
|
||||
|
||||
if (current_is_music()) {
|
||||
val *= audio_thread_->music_volume_ / 7.0f;
|
||||
val *= audio_server_->music_volume_ / 7.0f;
|
||||
} else {
|
||||
val *= audio_thread_->sound_volume_;
|
||||
val *= audio_server_->sound_volume_;
|
||||
}
|
||||
alSourcef(source_, AL_GAIN, std::max(0.0f, val));
|
||||
CHECK_AL_ERROR;
|
||||
@ -1208,7 +1225,7 @@ void AudioServer::ThreadSource_::UpdatePitch() {
|
||||
float val = 1.0f;
|
||||
if (current_is_music()) {
|
||||
} else {
|
||||
val *= audio_thread_->sound_pitch_;
|
||||
val *= audio_server_->sound_pitch_;
|
||||
}
|
||||
alSourcef(source_, AL_PITCH, val);
|
||||
CHECK_AL_ERROR;
|
||||
|
||||
@ -115,8 +115,10 @@ class AudioServer {
|
||||
float sound_volume_{1.0f};
|
||||
float sound_pitch_{1.0f};
|
||||
float music_volume_{1.0f};
|
||||
float app_active_volume_{1.0f};
|
||||
|
||||
bool have_pending_loads_{};
|
||||
bool app_active_{true};
|
||||
bool suspended_{};
|
||||
bool shutdown_completed_{};
|
||||
bool shutting_down_{};
|
||||
|
||||
@ -250,6 +250,166 @@ void BaseFeatureSet::StartApp() {
|
||||
g_core->LifecycleLog("start-app end (main thread)");
|
||||
}
|
||||
|
||||
void BaseFeatureSet::SuspendApp() {
|
||||
assert(g_core);
|
||||
assert(g_core->InMainThread());
|
||||
|
||||
if (app_suspended_) {
|
||||
Log(LogLevel::kWarning,
|
||||
"AppAdapter::SuspendApp() called with app already suspended.");
|
||||
return;
|
||||
}
|
||||
|
||||
millisecs_t start_time{core::CorePlatform::GetCurrentMillisecs()};
|
||||
|
||||
// Apple mentioned 5 seconds to run stuff once backgrounded or they bring
|
||||
// down the hammer. Let's aim to stay under 2.
|
||||
millisecs_t max_duration{2000};
|
||||
|
||||
g_core->platform->LowLevelDebugLog(
|
||||
"SuspendApp@"
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs()));
|
||||
app_suspended_ = true;
|
||||
|
||||
// IMPORTANT: Any pause related stuff that event-loop-threads need to do
|
||||
// should be done from their registered pause-callbacks. If we instead
|
||||
// push runnables to them from here they may or may not be called before
|
||||
// their event-loop is actually paused.
|
||||
|
||||
// Pause all event loops.
|
||||
EventLoop::SetEventLoopsSuspended(true);
|
||||
|
||||
if (g_base->network_reader) {
|
||||
g_base->network_reader->OnAppSuspend();
|
||||
}
|
||||
g_base->networking->OnAppSuspend();
|
||||
|
||||
// We assume that the OS will completely suspend our process the moment we
|
||||
// return from this call (though this is not technically true on all
|
||||
// platforms). So we want to spin here and give our various event loop
|
||||
// threads time to park themselves.
|
||||
std::vector<EventLoop*> running_loops;
|
||||
do {
|
||||
// If/when we get to a point with no threads waiting to be paused, we're
|
||||
// good to go.
|
||||
// auto loops{EventLoop::GetStillSuspendingEventLoops()};
|
||||
running_loops = EventLoop::GetStillSuspendingEventLoops();
|
||||
// running_loop_count = loops.size();
|
||||
if (running_loops.empty()) {
|
||||
if (g_buildconfig.debug_build()) {
|
||||
Log(LogLevel::kDebug,
|
||||
"SuspendApp() completed in "
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs()
|
||||
- start_time)
|
||||
+ "ms.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
} while (std::abs(core::CorePlatform::GetCurrentMillisecs() - start_time)
|
||||
< max_duration);
|
||||
|
||||
// If we made it here, we timed out. Complain.
|
||||
std::string msg =
|
||||
std::string("SuspendApp() took too long; ")
|
||||
+ std::to_string(running_loops.size())
|
||||
+ " event-loops not yet suspended after "
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs() - start_time)
|
||||
+ " ms: (";
|
||||
bool first = true;
|
||||
for (auto* loop : running_loops) {
|
||||
if (!first) {
|
||||
msg += ", ";
|
||||
}
|
||||
// Note: not adding a default here so compiler complains if we
|
||||
// add/change something.
|
||||
switch (loop->identifier()) {
|
||||
case EventLoopID::kInvalid:
|
||||
msg += "invalid";
|
||||
break;
|
||||
case EventLoopID::kLogic:
|
||||
msg += "logic";
|
||||
break;
|
||||
case EventLoopID::kAssets:
|
||||
msg += "assets";
|
||||
break;
|
||||
case EventLoopID::kFileOut:
|
||||
msg += "fileout";
|
||||
break;
|
||||
case EventLoopID::kMain:
|
||||
msg += "main";
|
||||
break;
|
||||
case EventLoopID::kAudio:
|
||||
msg += "audio";
|
||||
break;
|
||||
case EventLoopID::kNetworkWrite:
|
||||
msg += "networkwrite";
|
||||
break;
|
||||
case EventLoopID::kSuicide:
|
||||
msg += "suicide";
|
||||
break;
|
||||
case EventLoopID::kStdin:
|
||||
msg += "stdin";
|
||||
break;
|
||||
case EventLoopID::kBGDynamics:
|
||||
msg += "bgdynamics";
|
||||
break;
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
msg += ").";
|
||||
|
||||
Log(LogLevel::kError, msg);
|
||||
}
|
||||
|
||||
void BaseFeatureSet::UnsuspendApp() {
|
||||
assert(g_core);
|
||||
assert(g_core->InMainThread());
|
||||
|
||||
if (!app_suspended_) {
|
||||
Log(LogLevel::kWarning,
|
||||
"AppAdapter::UnsuspendApp() called with app not in suspendedstate.");
|
||||
return;
|
||||
}
|
||||
millisecs_t start_time{core::CorePlatform::GetCurrentMillisecs()};
|
||||
g_core->platform->LowLevelDebugLog(
|
||||
"UnsuspendApp@"
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs()));
|
||||
app_suspended_ = false;
|
||||
|
||||
// Spin all event-loops back up.
|
||||
EventLoop::SetEventLoopsSuspended(false);
|
||||
|
||||
// Run resumes that expect to happen in the main thread.
|
||||
g_base->network_reader->OnAppUnsuspend();
|
||||
g_base->networking->OnAppUnsuspend();
|
||||
|
||||
// When resuming from a suspended state, we may want to pause whatever
|
||||
// game was running when we last were active.
|
||||
//
|
||||
// TODO(efro): we should make this smarter so it doesn't happen if we're
|
||||
// in a network game or something that we can't pause; bringing up the
|
||||
// menu doesn't really accomplish anything there.
|
||||
//
|
||||
// In general this probably should be handled at a higher level.
|
||||
// if (g_core->should_pause_active_game) {
|
||||
// g_core->should_pause_active_game = false;
|
||||
|
||||
// // If we've been completely backgrounded, send a menu-press command to
|
||||
// // the game; this will bring up a pause menu if we're in the game/etc.
|
||||
// if (!g_base->ui->MainMenuVisible()) {
|
||||
// g_base->ui->PushMainMenuPressCall(nullptr);
|
||||
// }
|
||||
// }
|
||||
|
||||
if (g_buildconfig.debug_build()) {
|
||||
Log(LogLevel::kDebug,
|
||||
"UnsuspendApp() completed in "
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs()
|
||||
- start_time)
|
||||
+ "ms.");
|
||||
}
|
||||
}
|
||||
|
||||
void BaseFeatureSet::OnAppShutdownComplete() {
|
||||
assert(g_core->InMainThread());
|
||||
assert(g_core);
|
||||
@ -760,4 +920,57 @@ void BaseFeatureSet::PushMainThreadRunnable(Runnable* runnable) {
|
||||
app_adapter->DoPushMainThreadRunnable(runnable);
|
||||
}
|
||||
|
||||
auto BaseFeatureSet::ClipboardIsSupported() -> bool {
|
||||
// We only call our actual virtual function once.
|
||||
if (!have_clipboard_is_supported_) {
|
||||
clipboard_is_supported_ = app_adapter->DoClipboardIsSupported();
|
||||
have_clipboard_is_supported_ = true;
|
||||
}
|
||||
return clipboard_is_supported_;
|
||||
}
|
||||
|
||||
auto BaseFeatureSet::ClipboardHasText() -> bool {
|
||||
// If subplatform says they don't support clipboards, don't even ask.
|
||||
if (!ClipboardIsSupported()) {
|
||||
return false;
|
||||
}
|
||||
return app_adapter->DoClipboardHasText();
|
||||
}
|
||||
|
||||
void BaseFeatureSet::ClipboardSetText(const std::string& text) {
|
||||
// If subplatform says they don't support clipboards, this is an error.
|
||||
if (!ClipboardIsSupported()) {
|
||||
throw Exception("ClipboardSetText called with no clipboard support.",
|
||||
PyExcType::kRuntime);
|
||||
}
|
||||
app_adapter->DoClipboardSetText(text);
|
||||
}
|
||||
|
||||
auto BaseFeatureSet::ClipboardGetText() -> std::string {
|
||||
// If subplatform says they don't support clipboards, this is an error.
|
||||
if (!ClipboardIsSupported()) {
|
||||
throw Exception("ClipboardGetText called with no clipboard support.",
|
||||
PyExcType::kRuntime);
|
||||
}
|
||||
return app_adapter->DoClipboardGetText();
|
||||
}
|
||||
|
||||
void BaseFeatureSet::SetAppActive(bool active) {
|
||||
assert(InMainThread());
|
||||
g_core->platform->LowLevelDebugLog(
|
||||
"SetAppActive(" + std::to_string(active) + ")@"
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs()));
|
||||
|
||||
printf("APP ACTIVE %d\n", static_cast<int>(active));
|
||||
|
||||
// Issue a gentle warning if they are feeding us the same state twice in a
|
||||
// row; might imply faulty logic.
|
||||
if (app_active_set_ && app_active_ == active) {
|
||||
Log(LogLevel::kWarning, "SetAppActive called with state "
|
||||
+ std::to_string(active) + " twice in a row.");
|
||||
}
|
||||
app_active_set_ = true;
|
||||
app_active_ = active;
|
||||
}
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
@ -609,6 +609,31 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
|
||||
/// Start app systems in motion.
|
||||
void StartApp() override;
|
||||
|
||||
/// Set the app's active state. Should be called from the main thread.
|
||||
/// Generally called by the AppAdapter. Being inactive means the app
|
||||
/// experience is not front and center and thus it may want to throttle
|
||||
/// down its rendering rate, pause single play gameplay, etc. This does
|
||||
/// not, however, cause any extreme action such as halting event loops;
|
||||
/// use Suspend/Resume for that. And note that the app may still be
|
||||
/// visible while inactive, so it should not *completely* stop
|
||||
/// drawing/etc.
|
||||
void SetAppActive(bool active);
|
||||
|
||||
/// Put the app into a suspended state. Should be called from the main
|
||||
/// thread. Generally called by the AppAdapter. Suspends event loops,
|
||||
/// closes network sockets, etc. Generally corresponds to being
|
||||
/// backgrounded on mobile platforms. It is assumed that, as soon as this
|
||||
/// call returns, all engine work is finished and all threads can be
|
||||
/// immediately suspended by the OS without any problems.
|
||||
void SuspendApp();
|
||||
|
||||
/// Return the app to a running state from a suspended one. Can correspond
|
||||
/// to foregrounding on mobile, unminimizing on desktop, etc. Spins
|
||||
/// threads back up, re-opens network sockets, etc.
|
||||
void UnsuspendApp();
|
||||
|
||||
auto app_suspended() const { return app_suspended_; }
|
||||
|
||||
/// Issue a high level app quit request. Can be called from any thread and
|
||||
/// can be safely called repeatedly. If 'confirm' is true, a confirmation
|
||||
/// dialog will be presented if the environment and situation allows;
|
||||
@ -738,6 +763,22 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
|
||||
/// reported by the Python layer.
|
||||
auto GetV2AccountID() -> std::optional<std::string>;
|
||||
|
||||
/// Return whether clipboard operations are supported at all. This gets
|
||||
/// called when determining whether to display clipboard related UI
|
||||
/// elements/etc.
|
||||
auto ClipboardIsSupported() -> bool;
|
||||
|
||||
/// Return whether there is currently text on the clipboard.
|
||||
auto ClipboardHasText() -> bool;
|
||||
|
||||
/// Set current clipboard text. Raises an Exception if clipboard is
|
||||
/// unsupported.
|
||||
void ClipboardSetText(const std::string& text);
|
||||
|
||||
/// Return current text from the clipboard. Raises an Exception if
|
||||
/// clipboard is unsupported or if there's no text on the clipboard.
|
||||
auto ClipboardGetText() -> std::string;
|
||||
|
||||
// Const subsystems.
|
||||
AppAdapter* const app_adapter;
|
||||
AppConfig* const app_config;
|
||||
@ -774,10 +815,7 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
|
||||
// Non-const bits (fixme: clean up access to these).
|
||||
TouchInput* touch_input{};
|
||||
|
||||
// auto return_value() const { return return_value_; }
|
||||
// void set_return_value(int val) { return_value_ = val; }
|
||||
|
||||
// auto GetReturnValue() const -> int override;
|
||||
auto app_active() const { return app_active_; }
|
||||
|
||||
private:
|
||||
BaseFeatureSet();
|
||||
@ -789,8 +827,12 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
|
||||
AppMode* app_mode_;
|
||||
PlusSoftInterface* plus_soft_{};
|
||||
ClassicSoftInterface* classic_soft_{};
|
||||
|
||||
std::mutex shutdown_suppress_lock_;
|
||||
bool have_clipboard_is_supported_{};
|
||||
bool clipboard_is_supported_{};
|
||||
bool app_active_set_{};
|
||||
bool app_active_{true};
|
||||
bool app_suspended_{};
|
||||
bool shutdown_suppress_disallowed_{};
|
||||
bool tried_importing_plus_{};
|
||||
bool tried_importing_classic_{};
|
||||
@ -803,7 +845,6 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
|
||||
bool basn_log_behavior_{};
|
||||
bool server_wrapper_managed_{};
|
||||
int shutdown_suppress_count_{};
|
||||
// int return_value_{};
|
||||
};
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
@ -83,12 +83,12 @@ Graphics::~Graphics() = default;
|
||||
|
||||
void Graphics::OnAppStart() { assert(g_base->InLogicThread()); }
|
||||
|
||||
void Graphics::OnAppPause() {
|
||||
void Graphics::OnAppSuspend() {
|
||||
assert(g_base->InLogicThread());
|
||||
SetGyroEnabled(false);
|
||||
}
|
||||
|
||||
void Graphics::OnAppResume() {
|
||||
void Graphics::OnAppUnsuspend() {
|
||||
assert(g_base->InLogicThread());
|
||||
g_base->graphics->SetGyroEnabled(true);
|
||||
}
|
||||
@ -993,7 +993,10 @@ void Graphics::DrawFades(FrameDef* frame_def) {
|
||||
// Guard against accidental fades that never fade back in.
|
||||
if (fade_ <= 0.0f && fade_out_) {
|
||||
millisecs_t faded_time = real_time - (fade_start_ + fade_time_);
|
||||
if (faded_time > 15000) {
|
||||
|
||||
// TEMP HACK - don't trigger this while inactive.
|
||||
// Need to make overall fade logic smarter.
|
||||
if (faded_time > 15000 && g_base->app_active()) {
|
||||
Log(LogLevel::kError, "FORCE-ENDING STUCK FADE");
|
||||
fade_out_ = false;
|
||||
fade_ = 1.0f;
|
||||
|
||||
@ -56,8 +56,8 @@ class Graphics {
|
||||
Graphics();
|
||||
|
||||
void OnAppStart();
|
||||
void OnAppPause();
|
||||
void OnAppResume();
|
||||
void OnAppSuspend();
|
||||
void OnAppUnsuspend();
|
||||
void OnAppShutdown();
|
||||
void OnAppShutdownComplete();
|
||||
void OnScreenSizeChange();
|
||||
|
||||
@ -143,7 +143,7 @@ auto GraphicsServer::WaitForRenderFrameDef_() -> FrameDef* {
|
||||
// Spin and wait for a short bit for a frame_def to appear.
|
||||
while (true) {
|
||||
// Stop waiting if we can't/shouldn't render anyway.
|
||||
if (!renderer_ || shutting_down_ || g_base->app_adapter->app_suspended()) {
|
||||
if (!renderer_ || shutting_down_ || g_base->app_suspended()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@ -516,9 +516,9 @@ void Input::OnAppStart() {
|
||||
}
|
||||
}
|
||||
|
||||
void Input::OnAppPause() { assert(g_base->InLogicThread()); }
|
||||
void Input::OnAppSuspend() { assert(g_base->InLogicThread()); }
|
||||
|
||||
void Input::OnAppResume() { assert(g_base->InLogicThread()); }
|
||||
void Input::OnAppUnsuspend() { assert(g_base->InLogicThread()); }
|
||||
|
||||
void Input::OnAppShutdown() { assert(g_base->InLogicThread()); }
|
||||
|
||||
|
||||
@ -21,8 +21,8 @@ class Input {
|
||||
Input();
|
||||
|
||||
void OnAppStart();
|
||||
void OnAppPause();
|
||||
void OnAppResume();
|
||||
void OnAppSuspend();
|
||||
void OnAppUnsuspend();
|
||||
void OnAppShutdown();
|
||||
void OnAppShutdownComplete();
|
||||
void StepDisplayTime();
|
||||
|
||||
@ -48,9 +48,9 @@ void Logic::OnAppStart() {
|
||||
|
||||
// Stay informed when our event loop is pausing/unpausing.
|
||||
event_loop_->AddSuspendCallback(
|
||||
NewLambdaRunnableUnmanaged([this] { OnAppPause(); }));
|
||||
NewLambdaRunnableUnmanaged([this] { OnAppSuspend(); }));
|
||||
event_loop_->AddUnsuspendCallback(
|
||||
NewLambdaRunnableUnmanaged([this] { OnAppResume(); }));
|
||||
NewLambdaRunnableUnmanaged([this] { OnAppUnsuspend(); }));
|
||||
|
||||
// Running in a specific order here and should try to stick to it in
|
||||
// other OnAppXXX callbacks so any subsystem interdependencies behave
|
||||
@ -179,40 +179,40 @@ void Logic::OnInitialAppModeSet() {
|
||||
}
|
||||
}
|
||||
|
||||
void Logic::OnAppPause() {
|
||||
void Logic::OnAppSuspend() {
|
||||
assert(g_base->InLogicThread());
|
||||
assert(g_base->CurrentContext().IsEmpty());
|
||||
|
||||
// Note: keep these in opposite order of OnAppStart.
|
||||
g_base->python->OnAppPause();
|
||||
g_base->python->OnAppSuspend();
|
||||
if (g_base->HavePlus()) {
|
||||
g_base->plus()->OnAppPause();
|
||||
g_base->plus()->OnAppSuspend();
|
||||
}
|
||||
g_base->app_mode()->OnAppPause();
|
||||
g_base->ui->OnAppPause();
|
||||
g_base->input->OnAppPause();
|
||||
g_base->audio->OnAppPause();
|
||||
g_base->graphics->OnAppPause();
|
||||
g_base->platform->OnAppPause();
|
||||
g_base->app_adapter->OnAppPause();
|
||||
g_base->app_mode()->OnAppSuspend();
|
||||
g_base->ui->OnAppSuspend();
|
||||
g_base->input->OnAppSuspend();
|
||||
g_base->audio->OnAppSuspend();
|
||||
g_base->graphics->OnAppSuspend();
|
||||
g_base->platform->OnAppSuspend();
|
||||
g_base->app_adapter->OnAppSuspend();
|
||||
}
|
||||
|
||||
void Logic::OnAppResume() {
|
||||
void Logic::OnAppUnsuspend() {
|
||||
assert(g_base->InLogicThread());
|
||||
assert(g_base->CurrentContext().IsEmpty());
|
||||
|
||||
// Note: keep these in the same order as OnAppStart.
|
||||
g_base->app_adapter->OnAppResume();
|
||||
g_base->platform->OnAppResume();
|
||||
g_base->graphics->OnAppResume();
|
||||
g_base->audio->OnAppResume();
|
||||
g_base->input->OnAppResume();
|
||||
g_base->ui->OnAppResume();
|
||||
g_base->app_mode()->OnAppResume();
|
||||
g_base->app_adapter->OnAppUnsuspend();
|
||||
g_base->platform->OnAppUnsuspend();
|
||||
g_base->graphics->OnAppUnsuspend();
|
||||
g_base->audio->OnAppUnsuspend();
|
||||
g_base->input->OnAppUnsuspend();
|
||||
g_base->ui->OnAppUnsuspend();
|
||||
g_base->app_mode()->OnAppUnsuspend();
|
||||
if (g_base->HavePlus()) {
|
||||
g_base->plus()->OnAppResume();
|
||||
g_base->plus()->OnAppUnsuspend();
|
||||
}
|
||||
g_base->python->OnAppResume();
|
||||
g_base->python->OnAppUnsuspend();
|
||||
}
|
||||
|
||||
void Logic::Shutdown() {
|
||||
|
||||
@ -52,11 +52,11 @@ class Logic {
|
||||
|
||||
/// Called when our event-loop pauses. Informs Python and other
|
||||
/// subsystems.
|
||||
void OnAppPause();
|
||||
void OnAppSuspend();
|
||||
|
||||
/// Called when our event-loop resumes. Informs Python and other
|
||||
/// subsystems.
|
||||
void OnAppResume();
|
||||
void OnAppUnsuspend();
|
||||
|
||||
void OnAppShutdown();
|
||||
void OnAppShutdownComplete();
|
||||
|
||||
@ -25,7 +25,7 @@ void NetworkReader::SetPort(int port) {
|
||||
thread_ = new std::thread(RunThreadStatic_, this);
|
||||
}
|
||||
|
||||
void NetworkReader::OnAppPause() {
|
||||
void NetworkReader::OnAppSuspend() {
|
||||
assert(g_core->InMainThread());
|
||||
assert(!paused_);
|
||||
{
|
||||
@ -42,7 +42,7 @@ void NetworkReader::OnAppPause() {
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkReader::OnAppResume() {
|
||||
void NetworkReader::OnAppUnsuspend() {
|
||||
assert(g_core->InMainThread());
|
||||
assert(paused_);
|
||||
|
||||
|
||||
@ -24,8 +24,8 @@ class NetworkReader {
|
||||
public:
|
||||
NetworkReader();
|
||||
void SetPort(int port);
|
||||
void OnAppPause();
|
||||
void OnAppResume();
|
||||
void OnAppSuspend();
|
||||
void OnAppUnsuspend();
|
||||
auto port4() const { return port4_; }
|
||||
auto port6() const { return port6_; }
|
||||
auto sd_mutex() -> std::mutex& { return sd_mutex_; }
|
||||
|
||||
@ -31,9 +31,9 @@ void Networking::DoApplyAppConfig() {
|
||||
}
|
||||
}
|
||||
|
||||
void Networking::OnAppPause() {}
|
||||
void Networking::OnAppSuspend() {}
|
||||
|
||||
void Networking::OnAppResume() {}
|
||||
void Networking::OnAppUnsuspend() {}
|
||||
|
||||
void Networking::SendTo(const std::vector<uint8_t>& buffer,
|
||||
const SockAddr& addr) {
|
||||
|
||||
@ -127,8 +127,8 @@ class Networking {
|
||||
|
||||
// Called on mobile platforms when going into the background, etc
|
||||
// (when all networking should be shut down)
|
||||
void OnAppPause();
|
||||
void OnAppResume();
|
||||
void OnAppSuspend();
|
||||
void OnAppUnsuspend();
|
||||
|
||||
auto remote_server_accepting_connections() -> bool {
|
||||
return remote_server_accepting_connections_;
|
||||
|
||||
@ -166,8 +166,8 @@ void BasePlatform::SetupInterruptHandling() {
|
||||
}
|
||||
|
||||
void BasePlatform::OnAppStart() { assert(g_base->InLogicThread()); }
|
||||
void BasePlatform::OnAppPause() { assert(g_base->InLogicThread()); }
|
||||
void BasePlatform::OnAppResume() { assert(g_base->InLogicThread()); }
|
||||
void BasePlatform::OnAppSuspend() { assert(g_base->InLogicThread()); }
|
||||
void BasePlatform::OnAppUnsuspend() { assert(g_base->InLogicThread()); }
|
||||
void BasePlatform::OnAppShutdown() { assert(g_base->InLogicThread()); }
|
||||
void BasePlatform::OnAppShutdownComplete() { assert(g_base->InLogicThread()); }
|
||||
void BasePlatform::OnScreenSizeChange() { assert(g_base->InLogicThread()); }
|
||||
|
||||
@ -26,8 +26,8 @@ class BasePlatform {
|
||||
|
||||
// Logic thread callbacks.
|
||||
virtual void OnAppStart();
|
||||
virtual void OnAppPause();
|
||||
virtual void OnAppResume();
|
||||
virtual void OnAppSuspend();
|
||||
virtual void OnAppUnsuspend();
|
||||
virtual void OnAppShutdown();
|
||||
virtual void OnAppShutdownComplete();
|
||||
virtual void OnScreenSizeChange();
|
||||
|
||||
@ -82,7 +82,7 @@ static const char* const scancode_names[SDL_NUM_SCANCODES] = {
|
||||
"F12",
|
||||
"PrintScreen",
|
||||
"ScrollLock",
|
||||
"OnAppPause",
|
||||
"Pause",
|
||||
"Insert",
|
||||
"Home",
|
||||
"PageUp",
|
||||
|
||||
@ -149,12 +149,12 @@ void BasePython::OnAppStart() {
|
||||
objs().Get(BasePython::ObjID::kAppOnNativeStartCall).Call();
|
||||
}
|
||||
|
||||
void BasePython::OnAppPause() {
|
||||
void BasePython::OnAppSuspend() {
|
||||
assert(g_base->InLogicThread());
|
||||
objs().Get(BasePython::ObjID::kAppOnNativeSuspendCall).Call();
|
||||
}
|
||||
|
||||
void BasePython::OnAppResume() {
|
||||
void BasePython::OnAppUnsuspend() {
|
||||
assert(g_base->InLogicThread());
|
||||
objs().Get(BasePython::ObjID::kAppOnNativeUnsuspendCall).Call();
|
||||
}
|
||||
|
||||
@ -15,8 +15,8 @@ class BasePython {
|
||||
|
||||
void OnMainThreadStartApp();
|
||||
void OnAppStart();
|
||||
void OnAppPause();
|
||||
void OnAppResume();
|
||||
void OnAppSuspend();
|
||||
void OnAppUnsuspend();
|
||||
void OnAppShutdown();
|
||||
void OnAppShutdownComplete();
|
||||
void DoApplyAppConfig();
|
||||
|
||||
@ -44,6 +44,27 @@ static PyMethodDef PyAppNameDef = {
|
||||
"(internal)\n",
|
||||
};
|
||||
|
||||
// ------------------------------ app_is_active --------------------------------
|
||||
|
||||
static auto PyAppIsActive(PyObject* self) -> PyObject* {
|
||||
BA_PYTHON_TRY;
|
||||
|
||||
if (g_base->app_active()) {
|
||||
Py_RETURN_TRUE;
|
||||
}
|
||||
Py_RETURN_FALSE;
|
||||
BA_PYTHON_CATCH;
|
||||
}
|
||||
|
||||
static PyMethodDef PyAppIsActiveDef = {
|
||||
"app_is_active", // name
|
||||
(PyCFunction)PyAppIsActive, // method
|
||||
METH_NOARGS, // flags
|
||||
|
||||
"app_is_active() -> bool\n"
|
||||
"\n"
|
||||
"(internal)\n",
|
||||
};
|
||||
// --------------------------------- run_app -----------------------------------
|
||||
|
||||
static auto PyRunApp(PyObject* self) -> PyObject* {
|
||||
@ -1650,6 +1671,7 @@ static PyMethodDef PyGraphicsShutdownIsCompleteDef = {
|
||||
auto PythonMethodsApp::GetMethods() -> std::vector<PyMethodDef> {
|
||||
return {
|
||||
PyAppNameDef,
|
||||
PyAppIsActiveDef,
|
||||
PyRunAppDef,
|
||||
PyAppNameUpperDef,
|
||||
PyIsXCodeBuildDef,
|
||||
|
||||
@ -129,7 +129,7 @@ static PyMethodDef PyHasTouchScreenDef = {
|
||||
|
||||
static auto PyClipboardIsSupported(PyObject* self) -> PyObject* {
|
||||
BA_PYTHON_TRY;
|
||||
if (g_base->app_adapter->ClipboardIsSupported()) {
|
||||
if (g_base->ClipboardIsSupported()) {
|
||||
Py_RETURN_TRUE;
|
||||
}
|
||||
Py_RETURN_FALSE;
|
||||
@ -155,7 +155,7 @@ static PyMethodDef PyClipboardIsSupportedDef = {
|
||||
|
||||
static auto PyClipboardHasText(PyObject* self) -> PyObject* {
|
||||
BA_PYTHON_TRY;
|
||||
if (g_base->app_adapter->ClipboardHasText()) {
|
||||
if (g_base->ClipboardHasText()) {
|
||||
Py_RETURN_TRUE;
|
||||
}
|
||||
Py_RETURN_FALSE;
|
||||
@ -188,7 +188,7 @@ static auto PyClipboardSetText(PyObject* self, PyObject* args, PyObject* keywds)
|
||||
const_cast<char**>(kwlist), &value)) {
|
||||
return nullptr;
|
||||
}
|
||||
g_base->app_adapter->ClipboardSetText(value);
|
||||
g_base->ClipboardSetText(value);
|
||||
Py_RETURN_NONE;
|
||||
BA_PYTHON_CATCH;
|
||||
}
|
||||
@ -212,7 +212,7 @@ static PyMethodDef PyClipboardSetTextDef = {
|
||||
|
||||
static auto PyClipboardGetText(PyObject* self) -> PyObject* {
|
||||
BA_PYTHON_TRY;
|
||||
return PyUnicode_FromString(g_base->app_adapter->ClipboardGetText().c_str());
|
||||
return PyUnicode_FromString(g_base->ClipboardGetText().c_str());
|
||||
Py_RETURN_FALSE;
|
||||
BA_PYTHON_CATCH;
|
||||
}
|
||||
|
||||
@ -19,8 +19,8 @@ namespace ballistica::base {
|
||||
class PlusSoftInterface {
|
||||
public:
|
||||
virtual void OnAppStart() = 0;
|
||||
virtual void OnAppPause() = 0;
|
||||
virtual void OnAppResume() = 0;
|
||||
virtual void OnAppSuspend() = 0;
|
||||
virtual void OnAppUnsuspend() = 0;
|
||||
virtual void OnAppShutdown() = 0;
|
||||
virtual void OnAppShutdownComplete() = 0;
|
||||
virtual void DoApplyAppConfig() = 0;
|
||||
|
||||
@ -1441,9 +1441,9 @@ void DevConsole::StepDisplayTime() {
|
||||
auto DevConsole::PasteFromClipboard() -> bool {
|
||||
if (state_ != State_::kInactive) {
|
||||
if (python_terminal_visible_) {
|
||||
if (g_base->app_adapter->ClipboardIsSupported()) {
|
||||
if (g_base->app_adapter->ClipboardHasText()) {
|
||||
auto text = g_base->app_adapter->ClipboardGetText();
|
||||
if (g_base->ClipboardIsSupported()) {
|
||||
if (g_base->ClipboardHasText()) {
|
||||
auto text = g_base->ClipboardGetText();
|
||||
if (strstr(text.c_str(), "\n") || strstr(text.c_str(), "\r")) {
|
||||
g_base->audio->PlaySound(
|
||||
g_base->assets->SysSound(SysSoundID::kErrorBeep));
|
||||
|
||||
@ -81,9 +81,9 @@ void UI::OnAppStart() {
|
||||
}
|
||||
}
|
||||
|
||||
void UI::OnAppPause() { assert(g_base->InLogicThread()); }
|
||||
void UI::OnAppSuspend() { assert(g_base->InLogicThread()); }
|
||||
|
||||
void UI::OnAppResume() {
|
||||
void UI::OnAppUnsuspend() {
|
||||
assert(g_base->InLogicThread());
|
||||
SetUIInputDevice(nullptr);
|
||||
}
|
||||
|
||||
@ -32,8 +32,8 @@ class UI {
|
||||
UI();
|
||||
|
||||
void OnAppStart();
|
||||
void OnAppPause();
|
||||
void OnAppResume();
|
||||
void OnAppSuspend();
|
||||
void OnAppUnsuspend();
|
||||
void OnAppShutdown();
|
||||
void OnAppShutdownComplete();
|
||||
void DoApplyAppConfig();
|
||||
|
||||
@ -103,7 +103,9 @@ auto CorePlatform::Create() -> CorePlatform* {
|
||||
return platform;
|
||||
}
|
||||
|
||||
void CorePlatform::DebugLog(const std::string& msg) { HandleDebugLog(msg); }
|
||||
void CorePlatform::LowLevelDebugLog(const std::string& msg) {
|
||||
HandleLowLevelDebugLog(msg);
|
||||
}
|
||||
|
||||
CorePlatform::CorePlatform() : start_time_millisecs_(GetCurrentMillisecs()) {}
|
||||
|
||||
@ -1021,7 +1023,7 @@ auto CorePlatform::HavePermission(Permission p) -> bool {
|
||||
void CorePlatform::SetDebugKey(const std::string& key,
|
||||
const std::string& value) {}
|
||||
|
||||
void CorePlatform::HandleDebugLog(const std::string& msg) {}
|
||||
void CorePlatform::HandleLowLevelDebugLog(const std::string& msg) {}
|
||||
|
||||
auto CorePlatform::GetCurrentMillisecs() -> millisecs_t {
|
||||
return std::chrono::time_point_cast<std::chrono::milliseconds>(
|
||||
|
||||
@ -54,9 +54,8 @@ class CorePlatform {
|
||||
/// fopen() supporting UTF8 strings.
|
||||
virtual auto FOpen(const char* path, const char* mode) -> FILE*;
|
||||
|
||||
/// rename() supporting UTF8 strings.
|
||||
/// For cross-platform consistency, this should also remove any file that
|
||||
/// exists at the target location first.
|
||||
/// rename() supporting UTF8 strings. For cross-platform consistency, this
|
||||
/// should also remove any file that exists at the target location first.
|
||||
virtual auto Rename(const char* oldname, const char* newname) -> int;
|
||||
|
||||
/// Simple cross-platform check for existence of a file.
|
||||
@ -332,26 +331,30 @@ class CorePlatform {
|
||||
/// This is expected to be lightweight as it may be called often.
|
||||
virtual void SetDebugKey(const std::string& key, const std::string& value);
|
||||
|
||||
void DebugLog(const std::string& msg);
|
||||
/// Print a log message to be included in crash logs or other debug
|
||||
/// mechanisms (example: Crashlytics). V1-cloud-log messages get forwarded
|
||||
/// to here as well. It can be useful to call this directly to report extra
|
||||
/// details that may help in debugging, as these calls are not considered
|
||||
/// 'noteworthy' or presented to the user as standard Log() calls are.
|
||||
void LowLevelDebugLog(const std::string& msg);
|
||||
|
||||
#pragma mark MISC --------------------------------------------------------------
|
||||
|
||||
/// Return a time measurement in milliseconds since launch.
|
||||
/// It *should* be monotonic.
|
||||
/// For most purposes, AppTime values are preferable since their progression
|
||||
/// pauses during app suspension and they are 100% guaranteed to not go
|
||||
/// backwards.
|
||||
/// Return a time measurement in milliseconds since launch. It *should* be
|
||||
/// monotonic. For most purposes, AppTime values are preferable since
|
||||
/// their progression pauses during app suspension and they are 100%
|
||||
/// guaranteed to not go backwards.
|
||||
auto GetTicks() const -> millisecs_t;
|
||||
|
||||
/// Return a raw current milliseconds value. It *should* be monotonic.
|
||||
/// It is relative to an undefined start point; only use it for time
|
||||
/// Return a raw current milliseconds value. It *should* be monotonic. It
|
||||
/// is relative to an undefined start point; only use it for time
|
||||
/// differences. Generally the AppTime values are preferable since their
|
||||
/// progression pauses during app suspension and they are 100% guaranteed
|
||||
/// to not go backwards.
|
||||
static auto GetCurrentMillisecs() -> millisecs_t;
|
||||
|
||||
/// Return a raw current microseconds value. It *should* be monotonic.
|
||||
/// It is relative to an undefined start point; only use it for time
|
||||
/// Return a raw current microseconds value. It *should* be monotonic. It
|
||||
/// is relative to an undefined start point; only use it for time
|
||||
/// differences. Generally the AppTime values are preferable since their
|
||||
/// progression pauses during app suspension and they are 100% guaranteed
|
||||
/// to not go backwards.
|
||||
@ -378,14 +381,15 @@ class CorePlatform {
|
||||
/// Is the OS currently playing music? (so we can avoid doing so).
|
||||
virtual auto IsOSPlayingMusic() -> bool;
|
||||
|
||||
/// Pass platform-specific misc-read-vals along to the OS (as a json string).
|
||||
/// Pass platform-specific misc-read-vals along to the OS (as a json
|
||||
/// string).
|
||||
virtual void SetPlatformMiscReadVals(const std::string& vals);
|
||||
|
||||
/// Set the name of the current thread (for debugging).
|
||||
virtual void SetCurrentThreadName(const std::string& name);
|
||||
|
||||
// If display-resolution can be directly set on this platform,
|
||||
// return true and set the native full res here. Otherwise return false;
|
||||
// If display-resolution can be directly set on this platform, return true
|
||||
// and set the native full res here. Otherwise return false;
|
||||
virtual auto GetDisplayResolution(int* x, int* y) -> bool;
|
||||
|
||||
/// Are we being run from a terminal? (should we show prompts, etc?).
|
||||
@ -412,44 +416,39 @@ class CorePlatform {
|
||||
/// device; something like "iPhone 12 Pro".
|
||||
virtual auto DoGetDeviceDescription() -> std::string;
|
||||
|
||||
/// Attempt to actually create a directory.
|
||||
/// Should *not* raise Exceptions if it already exists or if quiet is true.
|
||||
/// Attempt to actually create a directory. Should *not* raise Exceptions
|
||||
/// if it already exists or if quiet is true.
|
||||
virtual void DoMakeDir(const std::string& dir, bool quiet);
|
||||
|
||||
/// Attempt to actually get an abs path. This will only be called if
|
||||
/// the path is valid and exists.
|
||||
/// Attempt to actually get an abs path. This will only be called if the
|
||||
/// path is valid and exists.
|
||||
virtual auto DoAbsPath(const std::string& path, std::string* outpath) -> bool;
|
||||
|
||||
/// Calc the user scripts dir path for this platform.
|
||||
/// This will be called once and the path cached.
|
||||
/// Calc the user scripts dir path for this platform. This will be called
|
||||
/// once and the path cached.
|
||||
virtual auto DoGetUserPythonDirectoryMonolithicDefault()
|
||||
-> std::optional<std::string>;
|
||||
|
||||
/// Return the default config directory for this platform.
|
||||
/// This will be used as the config dir if not overridden via command
|
||||
/// line options, etc.
|
||||
/// Return the default config directory for this platform. This will be
|
||||
/// used as the config dir if not overridden via command line options,
|
||||
/// etc.
|
||||
virtual auto DoGetConfigDirectoryMonolithicDefault()
|
||||
-> std::optional<std::string>;
|
||||
|
||||
/// Return the default data directory for this platform.
|
||||
/// This will be used as the data dir if not overridden by core-config, etc.
|
||||
/// This is the one monolithic-default value that is not optional.
|
||||
/// Return the default data directory for this platform. This will be used
|
||||
/// as the data dir if not overridden by core-config, etc. This is the one
|
||||
/// monolithic-default value that is not optional.
|
||||
virtual auto DoGetDataDirectoryMonolithicDefault() -> std::string;
|
||||
|
||||
/// Return the default Volatile data dir for this platform.
|
||||
/// This will be used as the volatile-data-dir if not overridden via command
|
||||
/// line options/etc.
|
||||
/// Return the default Volatile data dir for this platform. This will be
|
||||
/// used as the volatile-data-dir if not overridden via command line
|
||||
/// options/etc.
|
||||
virtual auto GetDefaultVolatileDataDirectory() -> std::string;
|
||||
|
||||
/// Generate a random UUID string.
|
||||
virtual auto GenerateUUID() -> std::string;
|
||||
|
||||
/// Print a log message to be included in crash logs or other debug
|
||||
/// mechanisms (example: Crashlytics). V1-cloud-log messages get forwarded
|
||||
/// to here as well. It can be useful to call this directly to report extra
|
||||
/// details that may help in debugging, as these calls are not considered
|
||||
/// 'noteworthy' or presented to the user as standard Log() calls are.
|
||||
virtual void HandleDebugLog(const std::string& msg);
|
||||
virtual void HandleLowLevelDebugLog(const std::string& msg);
|
||||
|
||||
CorePlatform();
|
||||
virtual ~CorePlatform();
|
||||
|
||||
@ -10,9 +10,9 @@
|
||||
|
||||
namespace ballistica::core {
|
||||
|
||||
void LowLevelPythonDebugLog(const char* msg) {
|
||||
static void PythonLowLevelDebugLog_(const char* msg) {
|
||||
assert(g_core);
|
||||
g_core->platform->DebugLog(msg);
|
||||
g_core->platform->LowLevelDebugLog(msg);
|
||||
}
|
||||
|
||||
static void CheckPyInitStatus(const char* where, const PyStatus& status) {
|
||||
@ -28,7 +28,7 @@ void CorePython::InitPython() {
|
||||
|
||||
// Install our low level logger in our custom Python builds.
|
||||
#ifdef PY_HAVE_BALLISTICA_LOW_LEVEL_DEBUG_LOG
|
||||
Py_BallisticaLowLevelDebugLog = LowLevelPythonDebugLog;
|
||||
Py_BallisticaLowLevelDebugLog = PythonLowLevelDebugLog_;
|
||||
#endif
|
||||
|
||||
// Flip on some extra runtime debugging options in debug builds.
|
||||
|
||||
@ -106,14 +106,14 @@ void SceneV1AppMode::OnAppShutdown() {
|
||||
connections_->Shutdown();
|
||||
}
|
||||
|
||||
void SceneV1AppMode::OnAppPause() {
|
||||
void SceneV1AppMode::OnAppSuspend() {
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
// App is going into background or whatnot. Kill any sockets/etc.
|
||||
EndHostScanning();
|
||||
}
|
||||
|
||||
void SceneV1AppMode::OnAppResume() { assert(g_base->InLogicThread()); }
|
||||
void SceneV1AppMode::OnAppUnsuspend() { assert(g_base->InLogicThread()); }
|
||||
|
||||
// Note: for now we're making our host-scan network calls directly from the
|
||||
// logic thread. This is generally not a good idea since it appears that even
|
||||
|
||||
@ -148,8 +148,8 @@ class SceneV1AppMode : public base::AppMode {
|
||||
auto IsPlayerBanned(const PlayerSpec& spec) -> bool;
|
||||
void BanPlayer(const PlayerSpec& spec, millisecs_t duration);
|
||||
void OnAppStart() override;
|
||||
void OnAppPause() override;
|
||||
void OnAppResume() override;
|
||||
void OnAppSuspend() override;
|
||||
void OnAppUnsuspend() override;
|
||||
auto InClassicMainMenuSession() const -> bool override;
|
||||
auto CreateInputDeviceDelegate(base::InputDevice* device)
|
||||
-> base::InputDeviceDelegate* override;
|
||||
|
||||
@ -39,7 +39,7 @@ auto main(int argc, char** argv) -> int {
|
||||
namespace ballistica {
|
||||
|
||||
// These are set automatically via script; don't modify them here.
|
||||
const int kEngineBuildNumber = 21639;
|
||||
const int kEngineBuildNumber = 21652;
|
||||
const char* kEngineVersion = "1.7.30";
|
||||
const int kEngineApiVersion = 8;
|
||||
|
||||
|
||||
@ -38,7 +38,7 @@ void Logging::V1CloudLog(const std::string& msg) {
|
||||
|
||||
if (g_core) {
|
||||
// (ship to things like Crashlytics crash-logging)
|
||||
g_core->platform->DebugLog(msg);
|
||||
g_core->platform->LowLevelDebugLog(msg);
|
||||
|
||||
// Add to our complete v1-cloud-log.
|
||||
std::scoped_lock lock(g_core->v1_cloud_log_mutex);
|
||||
|
||||
@ -2416,9 +2416,9 @@ static auto PyShowAd(PyObject* self, PyObject* args, PyObject* keywds)
|
||||
static_cast<bool>(pass_actually_showed));
|
||||
|
||||
// In cases where we support ads, store our callback and kick one off.
|
||||
// We'll then fire our callback once its done.
|
||||
// If we *don't* support ads, just store our callback and then kick off
|
||||
// an ad-view-complete message ourself so the event flow is similar..
|
||||
// We'll then fire our callback once its done. If we *don't* support ads,
|
||||
// just store our callback and then kick off an ad-view-complete message
|
||||
// ourself so the event flow is similar..
|
||||
if (g_core->platform->GetHasAds()) {
|
||||
g_core->platform->ShowAd(purpose);
|
||||
} else {
|
||||
|
||||
@ -688,13 +688,14 @@ auto TextWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
|
||||
// If we're doing inline editing, handle clipboard paste.
|
||||
if (editable() && !ShouldUseStringEditor_()
|
||||
&& m.type == base::WidgetMessage::Type::kPaste) {
|
||||
if (g_base->app_adapter->ClipboardIsSupported()) {
|
||||
if (g_base->app_adapter->ClipboardHasText()) {
|
||||
if (g_base->ClipboardIsSupported()) {
|
||||
if (g_base->ClipboardHasText()) {
|
||||
// Just enter it char by char as if we had typed it...
|
||||
AddCharsToText_(g_base->app_adapter->ClipboardGetText());
|
||||
AddCharsToText_(g_base->ClipboardGetText());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we're doing inline editing, handle some key events.
|
||||
if (editable() && m.has_keysym && !ShouldUseStringEditor_()) {
|
||||
last_carat_change_time_millisecs_ =
|
||||
|
||||
@ -39,7 +39,7 @@ def build_openal(arch: str, mode: str) -> None:
|
||||
if mode not in MODES:
|
||||
raise CleanError(f"Invalid mode '{mode}'.")
|
||||
|
||||
enable_oboe = True
|
||||
# enable_oboe = True
|
||||
|
||||
# Get ndk path.
|
||||
ndk_path = (
|
||||
@ -51,9 +51,8 @@ def build_openal(arch: str, mode: str) -> None:
|
||||
.stdout.decode()
|
||||
.strip()
|
||||
)
|
||||
# os.environ['NDK_ROOT'] = ndk_path
|
||||
|
||||
# Grab from git and build.
|
||||
# Grab OpenALSoft
|
||||
builddir = _build_dir(arch, mode)
|
||||
subprocess.run(['rm', '-rf', builddir], check=True)
|
||||
subprocess.run(['mkdir', '-p', os.path.dirname(builddir)], check=True)
|
||||
@ -63,25 +62,20 @@ def build_openal(arch: str, mode: str) -> None:
|
||||
)
|
||||
subprocess.run(['git', 'checkout', '1.23.1'], check=True, cwd=builddir)
|
||||
|
||||
if enable_oboe:
|
||||
builddir_oboe = f'{builddir}_oboe'
|
||||
subprocess.run(['rm', '-rf', builddir_oboe], check=True)
|
||||
subprocess.run(
|
||||
['mkdir', '-p', os.path.dirname(builddir_oboe)], check=True
|
||||
)
|
||||
subprocess.run(
|
||||
[
|
||||
'git',
|
||||
'clone',
|
||||
'https://github.com/google/oboe',
|
||||
builddir_oboe,
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
subprocess.run(
|
||||
['git', 'checkout', '1.8.0'], check=True, cwd=builddir_oboe
|
||||
)
|
||||
print(f'FULLY GOT {builddir_oboe}')
|
||||
# Grab Oboe
|
||||
builddir_oboe = f'{builddir}_oboe'
|
||||
subprocess.run(['rm', '-rf', builddir_oboe], check=True)
|
||||
subprocess.run(['mkdir', '-p', os.path.dirname(builddir_oboe)], check=True)
|
||||
subprocess.run(
|
||||
[
|
||||
'git',
|
||||
'clone',
|
||||
'https://github.com/google/oboe',
|
||||
builddir_oboe,
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
subprocess.run(['git', 'checkout', '1.8.0'], check=True, cwd=builddir_oboe)
|
||||
|
||||
# One bit of filtering: by default, openalsoft sends all sorts of
|
||||
# log messages to the android log. This is reasonable since its
|
||||
@ -106,6 +100,67 @@ def build_openal(arch: str, mode: str) -> None:
|
||||
with open(loggingpath, 'w', encoding='utf-8') as outfile:
|
||||
outfile.write(txt)
|
||||
|
||||
# Add a function to set a logging function so we can gather info
|
||||
# on AL fatal errors/etc.
|
||||
# fpath = f'{builddir}/alc/alc.cpp'
|
||||
# with open(fpath, encoding='utf-8') as infile:
|
||||
# txt = infile.read()
|
||||
# txt = replace_exact(
|
||||
# txt,
|
||||
# 'ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device)\n',
|
||||
# (
|
||||
# 'void (*alcDebugLogger)(const char*) = nullptr;\n'
|
||||
# '\n'
|
||||
# 'ALC_API void ALC_APIENTRY'
|
||||
# ' alcSetDebugLogger(void (*fn)(const char*)) {\n'
|
||||
# ' alcDebugLogger = fn;\n'
|
||||
# '}\n'
|
||||
# '\n'
|
||||
# 'ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device)\n'
|
||||
# ),
|
||||
# )
|
||||
# with open(fpath, 'w', encoding='utf-8') as outfile:
|
||||
# outfile.write(txt)
|
||||
|
||||
# fpath = f'{builddir}/include/AL/alc.h'
|
||||
# with open(fpath, encoding='utf-8') as infile:
|
||||
# txt = infile.read()
|
||||
# txt = replace_exact(
|
||||
# txt,
|
||||
# 'ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device);\n',
|
||||
# 'ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device);\n'
|
||||
# 'ALC_API void ALC_APIENTRY alcSetDebugLogger('
|
||||
# 'void (*fn)(const char*));\n',
|
||||
# )
|
||||
# with open(fpath, 'w', encoding='utf-8') as outfile:
|
||||
# outfile.write(txt)
|
||||
|
||||
fpath = f'{builddir}/core/except.h'
|
||||
with open(fpath, encoding='utf-8') as infile:
|
||||
txt = infile.read()
|
||||
txt = replace_exact(
|
||||
txt,
|
||||
'#define END_API_FUNC catch(...) { std::terminate(); }\n',
|
||||
'#define END_API_FUNC\n',
|
||||
)
|
||||
txt = replace_exact(
|
||||
txt, '#define START_API_FUNC try\n', '#define START_API_FUNC\n'
|
||||
)
|
||||
# txt = replace_exact(
|
||||
# txt,
|
||||
# '#define END_API_FUNC catch(...) { std::terminate(); }\n',
|
||||
# 'extern void (*alcDebugLogger)(const char*);\n'
|
||||
# '\n'
|
||||
# '#define END_API_FUNC catch(...) { \\\n'
|
||||
# ' if (alcDebugLogger != nullptr) { \\\n'
|
||||
# ' alcDebugLogger("UNKNOWN OpenALSoft fatal exception."); \\\n'
|
||||
# ' } \\\n'
|
||||
# ' std::terminate(); \\\n'
|
||||
# '}\n'
|
||||
# )
|
||||
with open(fpath, 'w', encoding='utf-8') as outfile:
|
||||
outfile.write(txt)
|
||||
|
||||
android_platform = 23
|
||||
|
||||
subprocess.run(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user