From 0bdf00769ea99c0205724a9781710b77a33881db Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 23 Aug 2023 09:16:41 -0700 Subject: [PATCH] work on shutdown process and tidying --- .efrocachemap | 88 +++++++++---------- CHANGELOG.md | 29 ++++-- src/assets/ba_data/python/babase/_app.py | 28 +++++- src/assets/ba_data/python/baenv.py | 2 +- .../base/app_adapter/app_adapter_sdl.cc | 5 +- src/ballistica/base/input/input.cc | 11 +-- src/ballistica/base/logic/logic.cc | 49 ++++++++--- src/ballistica/base/logic/logic.h | 17 ++-- .../base/python/methods/python_methods_app.cc | 32 +++++-- src/ballistica/base/support/stdio_console.cc | 4 +- src/ballistica/base/ui/ui.cc | 37 ++++---- src/ballistica/base/ui/ui.h | 5 +- src/ballistica/core/core.h | 1 - src/ballistica/core/platform/core_platform.cc | 8 +- .../scene_v1/support/scene_v1_app_mode.cc | 2 +- src/ballistica/shared/ballistica.cc | 2 +- 16 files changed, 200 insertions(+), 120 deletions(-) diff --git a/.efrocachemap b/.efrocachemap index 3a1cf01f..2df785d1 100644 --- a/.efrocachemap +++ b/.efrocachemap @@ -4068,50 +4068,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": "242acbe77fb4b4dba000a4f2bfc05628", - "build/prefab/full/linux_arm64_gui/release/ballisticakit": "b3b950f7aef84bdcb905bc4862bc46c0", - "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "5bf6ff933f1202a16e59caab2614e7f5", - "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "5985564e7dcc9b37819c01f5617e07f5", - "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "3d490eb1e54ac4813f287c7aae7611cc", - "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "2cb1bebe253965580beacbc18aab4d19", - "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "39df8c4bf25de958ba7e0669b24fa851", - "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "1dd9de26acbf937ded40fdd1e31ba213", - "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "9b6675625e5d11bfbdb79156533b5e7a", - "build/prefab/full/mac_arm64_gui/release/ballisticakit": "c204a8de7b8b28ba0f8669d0b2de0828", - "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "3f62aa7d31a097a92404c022510f975b", - "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "724b0e3118a0fae86ab3686efc82a1f0", - "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "129aa7848d1bb8c5d9e1151185f752d6", - "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "20b89b0333e5de59becfe21c2092300c", - "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "5003b2740aed1c69b8296e5179d22d5b", - "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "d1e14b32cc026901a4e11c9e8c1715f1", - "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "2adbd526ecb26eba04c5165361c0a397", - "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "e7a3152446ef0fc03fc80609c25c824d", - "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "80fac001fbe4c6c3dcd9e64e38712394", - "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "61db43d2ee6123f4bdbb2a56eec29166", - "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "9cb423dc66c25d32365cf579bdb0ad48", - "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "dc03331ceb9a0bb57d74e6ab3ce69f16", - "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "9cb423dc66c25d32365cf579bdb0ad48", - "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "dc03331ceb9a0bb57d74e6ab3ce69f16", - "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "0a9803fd6e8750a0bec5036d848216a1", - "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "e353417db3536ee90e579b22f4eddb3e", - "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "0a9803fd6e8750a0bec5036d848216a1", - "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "e353417db3536ee90e579b22f4eddb3e", - "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "4cce38fc08e6a9aa47e0c57b23c930b5", - "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "5dd00571bd5aa72a4276d1b53cab4b2b", - "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "4cce38fc08e6a9aa47e0c57b23c930b5", - "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "5dd00571bd5aa72a4276d1b53cab4b2b", - "build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "faf824b0fd529e4bb24bf633eba3ac0d", - "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "f3f34fa0cd21bd378fa5711c627a9698", - "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "1272aaa24cce371db94f38661c8ab4f8", - "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "f3f34fa0cd21bd378fa5711c627a9698", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "b9ab40b7b1727328dd008b79397cafc4", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "80f77141b09264716c711d4c30190fc1", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "804a5e69223bb06d670ce6796526dee3", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "463170bfd59637e3b85bdbb7efd722ac", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "1acf5026c56619b9b73b32940be377f0", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "f3244a1acf2f49587c28a18852e09121", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "e2faf9da55a00991bfe97d052556b4fa", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "8ee526c52e834f8d29244a0f37e33183", + "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "372da2c7dbe0b51ab8f738ac49f34d9a", + "build/prefab/full/linux_arm64_gui/release/ballisticakit": "29f810028429f10574471ad85cdc9044", + "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "70ee4f5892e693e209d8a75420292ad0", + "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "fc0465f8ffcaf843859ee9def7e90b22", + "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "916f4d2fcf14d7e973694848602736c5", + "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "e20ef3249191a24b428fd30b585a6ab9", + "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "c1b483c4c1639af5ad247e9252f51956", + "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "742d5e4793a557f7d322267cdf25fde3", + "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "4880ca58a6b9ce36769657c0f8a5a4c5", + "build/prefab/full/mac_arm64_gui/release/ballisticakit": "c8084478951a16ba144f12d65386b9b3", + "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "06c0d46f6b991aed566a17d32c0cd85d", + "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "77607a1c853c968a04340e538c196f4d", + "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "358193a334d56860d284716432c95f1e", + "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "ee39adeed9e2f6405c83f5f81ccf54fc", + "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "9bc9ffe2da1a79524eb26a551ee50ba3", + "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "ed1c7c6769b381526f5ac8d9e16a8bad", + "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "ddce8a5ebc89ad56f23b24e2b04a5edc", + "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "8dc47367a8feb2908244f4b45ef332e8", + "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "d8fd33af6124baf1bfd8ab39372abf17", + "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "78801f3a9a811c0e62528b60b78e36e1", + "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "ee6e1bdccf0f3db9c198ba5632739103", + "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "2f84ca77788e244cdc3edaeb00a0dc4c", + "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "ee6e1bdccf0f3db9c198ba5632739103", + "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "2f84ca77788e244cdc3edaeb00a0dc4c", + "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "61c0c1aac4376682c8bd77ad7d279b3a", + "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "f6a37946765a6a866c9f894c4e7bb15c", + "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "61c0c1aac4376682c8bd77ad7d279b3a", + "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "f6a37946765a6a866c9f894c4e7bb15c", + "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "d18be4e09ecb9dbe9e299dcfc7183cdf", + "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "fdcaa83411b0ac9e165ab5b41552e03a", + "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "d18be4e09ecb9dbe9e299dcfc7183cdf", + "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "fdcaa83411b0ac9e165ab5b41552e03a", + "build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "bb7515a54eaab2d148575800c1250c47", + "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "e0c3972bcc77fa5fc5aed5802308b4b1", + "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "89cd2bd873934be3e46f409663e9c2cb", + "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "e0c3972bcc77fa5fc5aed5802308b4b1", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "89423e0c466b20a5bfbd660d85607264", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "0b1921de7f4505e9ad177b0be5fc5cb5", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "6fae4927a00f7e78822e1efdac4c421d", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "11178659f946fde59ad43af0c8a8aaac", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "59fa8ce09122d57e9ca64d28401f6f4a", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "9f2ada0e934bba54e04924f389243d09", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "1b5ca2f7ef5b934946d55486fe08bee3", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "9d03b912cadc179744712665bdeaf866", "src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c", "src/assets/ba_data/python/babase/_mgen/enums.py": "f8cd3af311ac63147882590123b78318", "src/ballistica/base/mgen/pyembed/binding_base.inc": "eeddad968b176000e31c65be6206a2bc", diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a34bf7f..6bdd350a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,21 +1,34 @@ -### 1.7.26 (build 21241, api 8, 2023-08-22) +### 1.7.26 (build 21244, api 8, 2023-08-23) +- Android should now be better at detecting hardware keyboards (you will see + 'Configure Keyboard' and 'Configure Keyboard P2' buttons under + Settings->Controllers if a hardware keyboard is detected). It can be a bit + tricky distinguishing between gamepad type devices and keyboards on Android, + so please holler if you have a gamepad that now suddenly thinks it is a + keyboard or anything like that. - Various general improvements to the pcommand (project command) system. - Modules containing pcommand functions are now named with an 's' - so `pcommands.py` instead of `pcommand.py`. `pcommand.py` in efrotools is now solely related to the functioning of the pcommand system. - Implemented the `pcommandbatch` system, which is a way to run pcommands using a simple local server/client architecture, and set up key build targets to use - it instead of regular pcommand. In some cases, such as when assembling build - assets, this can speed things up by 5x or so. If you run into any problems - that seem to be related to this, you can disable it by setting env var - `BA_PCOMMANDBATCH_DISABLE=1`. See docs in `tools/efrotools/pcommandbatch.py` - for more info. + that by default instead of regular pcommand. In some cases, such as when + assembling build assets, this can speed things up by 5x or so. Run `make + pcommandbatch_speed_test` to see what the theoretical biggest speedup is on + your system. If you run into any problems that seem to be related to this, you + can disable it by setting env var `BA_PCOMMANDBATCH_DISABLE=1` which will + cause everything to go use regular old pcommand. See docs in + `tools/efrotools/pcommandbatch.py` for more info. - Renamed the various `App` C++ classes to `AppAdapter` which better represents their current intended role. They are not a general interface to app functionality, but rather adapt the app to a particular paradigm or api (VR, - Headless, SDL GUI, etc.). Am trying to move any functionality out of those - classes that does not fit that definition. + Headless, SDL GUI, etc.). Also am trying to move any functionality out of + those classes that does not fit that definition. +- Started cleaning up the app exit process. This will allow the app to + gracefully run tasks such as syncing account data to the cloud or disk or + properly closing the audio system when shutting down. It also means there + should be more consistent use of the 'Quit?' confirm window. Please holler if + you see any odd behavior when trying to quit the app. ### 1.7.25 (build 21211, api 8, 2023-08-03) diff --git a/src/assets/ba_data/python/babase/_app.py b/src/assets/ba_data/python/babase/_app.py index 641e7a92..a748b91c 100644 --- a/src/assets/ba_data/python/babase/_app.py +++ b/src/assets/ba_data/python/babase/_app.py @@ -290,6 +290,7 @@ class App: self._pending_intent: AppIntent | None = None self._intent: AppIntent | None = None self._mode: AppMode | None = None + self._shutdown_task: asyncio.Task[None] | None = None # Controls which app-modes we use for handling given # app-intents. Plugins can override this to change high level @@ -705,11 +706,11 @@ class App: # pylint: disable=too-many-branches assert _babase.in_logic_thread() - if self._shutdown_called: + # Can't shut down until we've got our asyncio machinery spun up. + if self._shutdown_called and self._aioloop is not None: # Entering shutdown state: if self.state is not self.State.SHUTTING_DOWN: self.state = self.State.SHUTTING_DOWN - _babase.lifecyclelog('app state shutting down') self._on_app_shutdown() elif self._app_paused: @@ -748,6 +749,17 @@ class App: self._called_on_app_launching = True self._on_app_launching() + async def _shutdown(self) -> None: + import asyncio + + try: + # print('SHUTDOWN BEGIN') + await asyncio.sleep(0.0) + # print('SHUTDOWN END') + except Exception: + logging.exception('Error during shutdown.') + _babase.complete_shutdown() + def pause(self) -> None: """Should be called by the native layer when the app pauses.""" assert not self._app_paused # Should avoid redundant calls. @@ -761,7 +773,13 @@ class App: self._update_state() def shutdown(self) -> None: - """Should be called by the native layer when the app wants to quit.""" + """Called by the native layer when the app wants to quit. + + The app should use this notice to start cleaning up and shutting + down. Once shutdown is complete, it should call + _babase.complete_shutdown() which will trigger an actual exit + from the app. + """ self._shutdown_called = True self._update_state() @@ -806,6 +824,10 @@ class App: 'Error in on_app_shutdown for subsystem %s.', subsystem ) + # Now kick off any async shutdown task(s). + assert self._aioloop is not None + self._shutdown_task = self._aioloop.create_task(self._shutdown()) + def read_config(self) -> None: """(internal)""" from babase._appconfig import read_app_config diff --git a/src/assets/ba_data/python/baenv.py b/src/assets/ba_data/python/baenv.py index f5402095..9a01ca95 100644 --- a/src/assets/ba_data/python/baenv.py +++ b/src/assets/ba_data/python/baenv.py @@ -52,7 +52,7 @@ if TYPE_CHECKING: # Build number and version of the ballistica binary we expect to be # using. -TARGET_BALLISTICA_BUILD = 21241 +TARGET_BALLISTICA_BUILD = 21244 TARGET_BALLISTICA_VERSION = '1.7.26' diff --git a/src/ballistica/base/app_adapter/app_adapter_sdl.cc b/src/ballistica/base/app_adapter/app_adapter_sdl.cc index abe47748..c12de592 100644 --- a/src/ballistica/base/app_adapter/app_adapter_sdl.cc +++ b/src/ballistica/base/app_adapter/app_adapter_sdl.cc @@ -12,6 +12,7 @@ #include "ballistica/base/logic/logic.h" #include "ballistica/base/python/base_python.h" #include "ballistica/base/support/stress_test.h" +#include "ballistica/base/ui/ui.h" #include "ballistica/core/platform/core_platform.h" #include "ballistica/shared/foundation/event_loop.h" #include "ballistica/shared/python/python.h" @@ -159,7 +160,9 @@ void AppAdapterSDL::HandleSDLEvent(const SDL_Event& event) { #endif case SDL_QUIT: - g_base->logic->event_loop()->PushCall([] { g_base->logic->Shutdown(); }); + // g_base->logic->event_loop()->PushCall([] { g_base->logic->Shutdown(); + // }); + g_base->logic->event_loop()->PushCall([] { g_base->ui->ConfirmQuit(); }); break; #if BA_OSTYPE_MACOS && BA_XCODE_BUILD && !BA_HEADLESS_BUILD diff --git a/src/ballistica/base/input/input.cc b/src/ballistica/base/input/input.cc index 5847f3af..9fa0b25c 100644 --- a/src/ballistica/base/input/input.cc +++ b/src/ballistica/base/input/input.cc @@ -14,6 +14,7 @@ #include "ballistica/base/support/app_config.h" #include "ballistica/base/ui/console.h" #include "ballistica/base/ui/ui.h" +#include "ballistica/shared/buildconfig/buildconfig_common.h" #include "ballistica/shared/foundation/event_loop.h" #include "ballistica/shared/generic/utils.h" @@ -963,16 +964,16 @@ void Input::HandleKeyPress(const SDL_Keysym* keysym) { if (!g_buildconfig.ostype_ios_tvos() && !g_buildconfig.ostype_android()) { // Command-F or Control-F toggles full-screen. if (!repeat_press && keysym->sym == SDLK_f - && ((keysym->mod & KMOD_CTRL) || (keysym->mod & KMOD_GUI))) { // NOLINT + && ((keysym->mod & KMOD_CTRL) || (keysym->mod & KMOD_GUI))) { g_base->python->objs() .Get(BasePython::ObjID::kToggleFullscreenCall) .Call(); return; } - // Command-Q or Control-Q quits. - if (!repeat_press && keysym->sym == SDLK_q - && ((keysym->mod & KMOD_CTRL) || (keysym->mod & KMOD_GUI))) { // NOLINT + // Control-Q quits. On mac, the usual cmd-q gets handled by SDL/etc. + // implicitly. + if (!repeat_press && keysym->sym == SDLK_q && (keysym->mod & KMOD_CTRL)) { g_base->ui->ConfirmQuit(); return; } @@ -987,7 +988,7 @@ void Input::HandleKeyPress(const SDL_Keysym* keysym) { // Ctrl-V or Cmd-V sends paste commands to any interested text fields. // Command-Q or Control-Q quits. if (!repeat_press && keysym->sym == SDLK_v - && ((keysym->mod & KMOD_CTRL) || (keysym->mod & KMOD_GUI))) { // NOLINT + && ((keysym->mod & KMOD_CTRL) || (keysym->mod & KMOD_GUI))) { g_base->ui->SendWidgetMessage(WidgetMessage(WidgetMessage::Type::kPaste)); return; } diff --git a/src/ballistica/base/logic/logic.cc b/src/ballistica/base/logic/logic.cc index 2086c262..d95a8dbf 100644 --- a/src/ballistica/base/logic/logic.cc +++ b/src/ballistica/base/logic/logic.cc @@ -128,28 +128,58 @@ void Logic::OnAppResume() { g_base->python->OnAppResume(); } +void Logic::Shutdown() { + assert(g_base->InLogicThread()); + assert(g_base->IsAppStarted()); + + if (!shutting_down_) { + shutting_down_ = true; + OnAppShutdown(); + } +} + void Logic::OnAppShutdown() { assert(g_core); assert(g_base->CurrentContext().IsEmpty()); + assert(shutting_down_); + + g_core->LifecycleLog("app state shutting down"); // Nuke the app from orbit if we get stuck while shutting down. g_core->StartSuicideTimer("shutdown", 10000); - // Note: keep these in opposite order of OnAppStart. + // Let our subsystems know we're shutting down. + // Note: Keep these in opposite order of OnAppStart. + // Note2: Any shutdown processes that take a non-zero amount of time + // should be registered as shutdown-tasks g_base->python->OnAppShutdown(); if (g_base->HavePlus()) { g_base->plus()->OnAppShutdown(); } g_base->app_mode()->OnAppShutdown(); - g_core->platform->OnAppResume(); + g_core->platform->OnAppShutdown(); g_base->ui->OnAppShutdown(); g_base->input->OnAppShutdown(); g_base->audio->OnAppShutdown(); g_base->graphics->OnAppShutdown(); +} + +void Logic::CompleteShutdown() { + BA_PRECONDITION(g_base->InLogicThread()); + BA_PRECONDITION(shutting_down_); + BA_PRECONDITION(!shutdown_completed_); + + shutdown_completed_ = true; + OnAppShutdownComplete(); +} + +void Logic::OnAppShutdownComplete() { + assert(g_base->InLogicThread()); + + // Wrap up any last business here in the logic thread and then + // kick things over to the main thread to exit out of the main loop. + g_core->LifecycleLog("app shutdown complete"); - // FIXME: Should add a mechanism where we give the above subsystems - // a short bit of time to complete shutdown if they need it. - // For now just completing instantly. g_core->main_event_loop()->PushCall([] { g_base->OnAppShutdownComplete(); }); } @@ -592,15 +622,6 @@ void Logic::NotifyOfPendingAssetLoads() { UpdatePendingWorkTimer(); } -void Logic::Shutdown() { - assert(g_base->InLogicThread()); - - if (!g_core->shutting_down) { - g_core->shutting_down = true; - OnAppShutdown(); - } -} - auto Logic::NewAppTimer(millisecs_t length, bool repeat, const Object::Ref& runnable) -> int { // App-Timers simply get injected into our loop and run alongside our own diff --git a/src/ballistica/base/logic/logic.h b/src/ballistica/base/logic/logic.h index 93d33a8f..2dd3c27b 100644 --- a/src/ballistica/base/logic/logic.h +++ b/src/ballistica/base/logic/logic.h @@ -44,6 +44,7 @@ class Logic { void OnAppResume(); void OnAppShutdown(); + void OnAppShutdownComplete(); void OnAppModeChanged(); @@ -55,14 +56,14 @@ class Logic { /// graphical builds we also use this opportunity to step our logic. void Draw(); - /// Kick off a low level app shutdown which will result in the process - /// exiting. Platform-agnostic code should generally not call this - /// directly and should instead use high level calls like babase.quit(). - /// This allows platforms such as mobile to take alternate actions which - /// may involve leaving the underlying process running. FIXME: I feel like - /// this should be in one of the App classes. + /// Kick off an app shutdown. Shutdown is an asynchronous process which + /// may take a bit of time to complete. Safe to call repeatedly. void Shutdown(); + /// Should be called by the Python layer when it has completed all + /// shutdown related tasks. + void CompleteShutdown(); + /// Has CompleteAppBootstrapping been called? auto app_bootstrapping_complete() const { return app_bootstrapping_complete_; @@ -95,6 +96,8 @@ class Logic { } auto applied_app_config() const { return applied_app_config_; } + auto shutting_down() const { return shutting_down_; } + auto shutdown_completed() const { return shutdown_completed_; } private: void UpdateDisplayTimeForFrameDraw(); @@ -127,6 +130,8 @@ class Logic { bool have_pending_loads_{}; bool debug_log_display_time_{}; bool applied_app_config_{}; + bool shutting_down_{}; + bool shutdown_completed_{}; }; } // namespace ballistica::base diff --git a/src/ballistica/base/python/methods/python_methods_app.cc b/src/ballistica/base/python/methods/python_methods_app.cc index 9c397a65..c13b7a36 100644 --- a/src/ballistica/base/python/methods/python_methods_app.cc +++ b/src/ballistica/base/python/methods/python_methods_app.cc @@ -68,6 +68,28 @@ static PyMethodDef PyRunAppDef = { "manages its own event loop.", }; +// --------------------------- complete_shutdown ------------------------------- + +static auto PyCompleteShutdown(PyObject* self) -> PyObject* { + BA_PYTHON_TRY; + + assert(g_base); + g_base->logic->CompleteShutdown(); + + Py_RETURN_NONE; + BA_PYTHON_CATCH; +} + +static PyMethodDef PyCompleteShutdownDef = { + "complete_shutdown", // name + (PyCFunction)PyCompleteShutdown, // method + METH_NOARGS, // flags + + "complete_shutdown() -> None\n" + "\n" + "Complete the shutdown process, triggering the app to exit.\n", +}; + // -------------------------------- appnameupper ------------------------------- static auto PyAppNameUpper(PyObject* self) -> PyObject* { @@ -484,6 +506,8 @@ static PyMethodDef PyDisplayTimerDef = { static auto PyQuit(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* { BA_PYTHON_TRY; + BA_PRECONDITION(g_base->IsAppStarted()); + static const char* kwlist[] = {"soft", "back", nullptr}; int soft = 0; int back = 0; @@ -492,7 +516,7 @@ static auto PyQuit(PyObject* self, PyObject* args, PyObject* keywds) return nullptr; } - // FIXME this should all just go through platform. + // FIXME this should all just go through platform and/or app-adapter. if (g_buildconfig.ostype_ios_tvos()) { // This should never be called on iOS @@ -503,9 +527,6 @@ static auto PyQuit(PyObject* self, PyObject* args, PyObject* keywds) // A few types get handled specially on Android. if (g_buildconfig.ostype_android()) { -#pragma clang diagnostic push -#pragma ide diagnostic ignored "ConstantConditionsOC" - if (!handled && back) { // Back-quit simply synthesizes a back press. // Note to self: I remember this behaved slightly differently than @@ -514,8 +535,6 @@ static auto PyQuit(PyObject* self, PyObject* args, PyObject* keywds) handled = true; } -#pragma clang diagnostic pop - if (!handled && soft) { // Soft-quit just kills our activity but doesn't run app shutdown. // Thus we'll be able to spin back up (reset to the main menu) @@ -1517,6 +1536,7 @@ auto PythonMethodsApp::GetMethods() -> std::vector { PyEmptyAppModeHandleIntentDefaultDef, PyEmptyAppModeHandleIntentExecDef, PyGetImmediateReturnCodeDef, + PyCompleteShutdownDef, }; } diff --git a/src/ballistica/base/support/stdio_console.cc b/src/ballistica/base/support/stdio_console.cc index 50cc91e0..9840c63b 100644 --- a/src/ballistica/base/support/stdio_console.cc +++ b/src/ballistica/base/support/stdio_console.cc @@ -42,7 +42,7 @@ void StdioConsole::StartInMainThread() { // results of the last script-command message we may have just sent. if (stdin_is_terminal) { g_base->logic->event_loop()->PushCall([] { - if (!g_core->shutting_down) { + if (!g_base->logic->shutting_down()) { printf(">>> "); fflush(stdout); } @@ -97,7 +97,7 @@ void StdioConsole::StartInMainThread() { if (g_buildconfig.windows_console_build()) { core::CorePlatform::SleepMillisecs(250); } - if (!g_core->shutting_down) { + if (!g_base->logic->shutting_down()) { printf("Stdin EOF reached. Use Ctrl-C to quit.\n"); fflush(stdout); } diff --git a/src/ballistica/base/ui/ui.cc b/src/ballistica/base/ui/ui.cc index fb3fe804..2f7c4f3c 100644 --- a/src/ballistica/base/ui/ui.cc +++ b/src/ballistica/base/ui/ui.cc @@ -342,29 +342,24 @@ void UI::ShowURL(const std::string& url) { void UI::ConfirmQuit() { g_base->logic->event_loop()->PushCall([] { assert(g_base->InLogicThread()); - if (g_core->HeadlessMode()) { - Log(LogLevel::kError, "UI::ConfirmQuit() unhandled on headless."); + // If we're headless or input is locked or the in-app-console is up or + // we don't have ui-v1, just quit immediately; a confirm screen + // wouldn't work anyway. + if (g_core->HeadlessMode() || g_base->input->IsInputLocked() + || !g_base->HaveUIV1() + || (g_base->console() != nullptr && g_base->console()->active())) { + g_base->logic->Shutdown(); + // g_base->python->objs().Get(BasePython::ObjID::kQuitCall).Call(); + return; } else { - // If input is locked or the in-app-console is up or we don't have ui-v1, - // just quit immediately; a confirm screen wouldn't work anyway. - if (g_base->input->IsInputLocked() || !g_base->HaveUIV1() - || (g_base->console() != nullptr && g_base->console()->active())) { - // Just go through _babase.quit(). - // FIXME: Shouldn't need to go out to the Python layer here; - // once we've got a high level quit call in platform we can use - // that directly. - g_base->python->objs().Get(BasePython::ObjID::kQuitCall).Call(); - return; - } else { - ScopedSetContext ssc(nullptr); - g_base->audio->PlaySound(g_base->assets->SysSound(SysSoundID::kSwish)); - g_base->ui_v1()->DoQuitWindow(); + ScopedSetContext ssc(nullptr); + g_base->audio->PlaySound(g_base->assets->SysSound(SysSoundID::kSwish)); + g_base->ui_v1()->DoQuitWindow(); - // If we have a keyboard, give it UI ownership. - InputDevice* keyboard = g_base->input->keyboard_input(); - if (keyboard) { - g_base->ui->SetUIInputDevice(keyboard); - } + // If we have a keyboard, give it UI ownership. + InputDevice* keyboard = g_base->input->keyboard_input(); + if (keyboard) { + g_base->ui->SetUIInputDevice(keyboard); } } }); diff --git a/src/ballistica/base/ui/ui.h b/src/ballistica/base/ui/ui.h index f1d0dae3..932660e0 100644 --- a/src/ballistica/base/ui/ui.h +++ b/src/ballistica/base/ui/ui.h @@ -52,13 +52,14 @@ class UI { /// called from any thread. void ShowURL(const std::string& url); - /// High level call to request a quit ui (or in some cases quit - /// immediately). This can be called from any thread. + /// High level call to request a quit ui. When a UI can't be shown, + /// triggers an immediate shutdown. This can be called from any thread. void ConfirmQuit(); /// Return whether there is UI present in either the main or overlay /// stacks. Generally this implies the focus should be on the UI. auto MainMenuVisible() const -> bool; + auto PartyIconVisible() -> bool; void ActivatePartyIcon(); void HandleLegacyRootUIMouseMotion(float x, float y); diff --git a/src/ballistica/core/core.h b/src/ballistica/core/core.h index a17b01a0..fe812afa 100644 --- a/src/ballistica/core/core.h +++ b/src/ballistica/core/core.h @@ -160,7 +160,6 @@ class CoreFeatureSet { bool v1_cloud_log_full{}; int master_server_source{}; int session_count{}; - bool shutting_down{}; bool have_incentivized_ad{false}; bool should_pause{}; bool reset_vr_orientation{}; diff --git a/src/ballistica/core/platform/core_platform.cc b/src/ballistica/core/platform/core_platform.cc index 13fedcba..efcb80bd 100644 --- a/src/ballistica/core/platform/core_platform.cc +++ b/src/ballistica/core/platform/core_platform.cc @@ -645,19 +645,19 @@ auto CorePlatform::GetTextTextureData(void* tex) -> uint8_t* { } void CorePlatform::OnAppStart() { - // assert(g_base_soft && g_base_soft->InLogicThread()); + assert(g_base_soft && g_base_soft->InLogicThread()); } void CorePlatform::OnAppPause() { - // assert(g_base_soft && g_base_soft->InLogicThread()); + assert(g_base_soft && g_base_soft->InLogicThread()); } void CorePlatform::OnAppResume() { - // assert(g_base_soft && g_base_soft->InLogicThread()); + assert(g_base_soft && g_base_soft->InLogicThread()); } void CorePlatform::OnAppShutdown() { - // assert(g_base_soft && g_base_soft->InLogicThread()); + assert(g_base_soft && g_base_soft->InLogicThread()); } void CorePlatform::OnScreenSizeChange() { diff --git a/src/ballistica/scene_v1/support/scene_v1_app_mode.cc b/src/ballistica/scene_v1/support/scene_v1_app_mode.cc index 47f5c3dd..4754b498 100644 --- a/src/ballistica/scene_v1/support/scene_v1_app_mode.cc +++ b/src/ballistica/scene_v1/support/scene_v1_app_mode.cc @@ -1470,7 +1470,7 @@ void SceneV1AppMode::HandleGameQuery(const char* buffer, size_t size, void SceneV1AppMode::RunMainMenu() { assert(g_base->InLogicThread()); - if (g_core->shutting_down) { + if (g_base->logic->shutting_down()) { return; } assert(g_base->InLogicThread()); diff --git a/src/ballistica/shared/ballistica.cc b/src/ballistica/shared/ballistica.cc index 2a9a89e1..96727b16 100644 --- a/src/ballistica/shared/ballistica.cc +++ b/src/ballistica/shared/ballistica.cc @@ -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 = 21241; +const int kEngineBuildNumber = 21244; const char* kEngineVersion = "1.7.26"; #if BA_MONOLITHIC_BUILD