mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-01-19 21:37:57 +08:00
Merge branch 'efroemling:master' into master
This commit is contained in:
commit
192eb8d39e
102
.efrocachemap
generated
102
.efrocachemap
generated
@ -1416,10 +1416,10 @@
|
||||
"build/assets/ba_data/textures/crossOutMask.pvr": "94110cc4e3e47f81b68f548951a33c2b",
|
||||
"build/assets/ba_data/textures/crossOutMask_preview.png": "d5df4d494cfbf700e3c8726b3693716c",
|
||||
"build/assets/ba_data/textures/crossOut_preview.png": "a0628f1e6b7e9f7d3b73d1c835ec9286",
|
||||
"build/assets/ba_data/textures/cursor.dds": "575b05e3adc74adf5a5d4b482a54adc9",
|
||||
"build/assets/ba_data/textures/cursor.ktx": "56ef6481222c23cbc1ab0fe825f19b03",
|
||||
"build/assets/ba_data/textures/cursor.pvr": "344b8856a315af23f495ebd283ee54fa",
|
||||
"build/assets/ba_data/textures/cursor_preview.png": "0f6820abfe6b79b4133971ace8f3bc42",
|
||||
"build/assets/ba_data/textures/cursor.dds": "4655d0746ba75bcd5f44f47dde9e9fec",
|
||||
"build/assets/ba_data/textures/cursor.ktx": "7835afb0579c2ca3a6477314121f49a5",
|
||||
"build/assets/ba_data/textures/cursor.pvr": "18803c269b9b544d6c0606d7b9fb2d85",
|
||||
"build/assets/ba_data/textures/cursor_preview.png": "d7189625af474f06f1c953dd41e701a0",
|
||||
"build/assets/ba_data/textures/cuteSpaz.dds": "5876162f89e558a2220935a1d63493c3",
|
||||
"build/assets/ba_data/textures/cuteSpaz.ktx": "4a3bc3c1739991298d21a66256289d57",
|
||||
"build/assets/ba_data/textures/cuteSpaz.pvr": "a236803464dc49b61b63a5e83d305c4c",
|
||||
@ -4056,54 +4056,54 @@
|
||||
"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": "6d1f9c2c53c02a35d87bb0aea62f7408",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "cfbf3e80472077cbccc98b681d24e7cd",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "7af76def8c480e88ddd6257ab4d0dfff",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "6c3372ab5283cbd91362c23a32629ee5",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "80f3683bc192c94a6d1a1dc2137f0844",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "9fb5cc47cfd4bf4c717acf3877dde233",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "095a758aacb5940b793d82dddc142d34",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "f312f5066c8bc883a24b0e3a86884bb3",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "b9f8a78b14d439e8ace4c70e18ae9e19",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "5a25d4de9c3124822538602fb9273280",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "7ea84daf12222b10517bf71870b82b53",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "28dba23a69dc1159cf340f899f78f4d9",
|
||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "3b653a753ac0cb919431528908c9cccc",
|
||||
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "9df409c73fb121b4d5fcd0fde6d4e42f",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "be64f13a639454d8b82311dcfd6815e0",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "f713443831ee2d9f912ca3c5e10201d8",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "6135e6256411bf24e44834d3445f94af",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "e782b975adada7282b5aaa6b1b1b0e1d",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "142aa10ee8c2747b011ef18062d5de48",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "01d7543c88bf94b9478b261003c5521c",
|
||||
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "dd14e0abacf5a27d9823b0a41127e3e3",
|
||||
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "bd994ca8a1896ada5c582be155db5c36",
|
||||
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "dd14e0abacf5a27d9823b0a41127e3e3",
|
||||
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "bd994ca8a1896ada5c582be155db5c36",
|
||||
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "d855693f8342c4080ce0f452784e5cf9",
|
||||
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "eeaf4e383752fdcdaaae1cb863208870",
|
||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "d855693f8342c4080ce0f452784e5cf9",
|
||||
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "eeaf4e383752fdcdaaae1cb863208870",
|
||||
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "ba391e47cc87b609ea794cdf9e693163",
|
||||
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "84e913835ae280a59045bed189c920b0",
|
||||
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "ba391e47cc87b609ea794cdf9e693163",
|
||||
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "84e913835ae280a59045bed189c920b0",
|
||||
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "8f147ca53e6e261becb37d7ff5490b59",
|
||||
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "7d386ab4fc78cc7598188df82bf4f04a",
|
||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "5627e6b08b61024650420b849d41721b",
|
||||
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "7d386ab4fc78cc7598188df82bf4f04a",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "cc3c3f837962636cbcd542b9b54946f2",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "b6a9ccfc44c215d7f31e985bbc8eab06",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "056a71331f28fbe6a07ae4086e3e8391",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "ae9f5d7069310cb18f7dfc90ad207203",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "b014ca1f2fec594cb149f11d783ee165",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "2085834b3d1523f6e29cd89011d4c062",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "aa9fadf3a2410df7b5aca9c88194cfcc",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "d6ccc0796e64abab851caac47ce54a86",
|
||||
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "f0961021482a27c4904f33cf85ab86bb",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "b04eefee14228822208b6143048867de",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "83bde2fb0374153d753f917d6ab693d9",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "6765c40945fdecd8fd0eae796c2a1075",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "839e1cecbdec8364aea5b1c31fe1cf7b",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "2b3c91667b63f9fa6ecf807e021367e2",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "b8486cc3705974cf880a0c7bbd210902",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "a04084a19bbb6ef8111113ff7c003198",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "aef8e462af8e1d0439bf72ec84778611",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "f85863b0ea0145e7a51dd3f85d37663f",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "0dbc09374baeabbf20cf0a052be94b8b",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "67545befcd59c6764d2868c1aae24776",
|
||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "7b1a4d61fd25efb514dbc005c20369b5",
|
||||
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "5c755b14c9ec4938504cbffcd0369a25",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "b8229bd1716bd4787d1efd58f476c21f",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "146eb16386394bfc34baa40b31007c5d",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "d08c8a35cc951adb8c78f6b5ed61a41e",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "ecd356d753266666d36b67b018ed2b6e",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "2c9ad739ec8a2b0f333ef7d0f38cd7b0",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "e9c2a5c54ecc34cf35a8db42367ddb3d",
|
||||
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "58946f3534363d88f713c54d3d643d6d",
|
||||
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "be356d05ecccd68043258d87b1892805",
|
||||
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "58946f3534363d88f713c54d3d643d6d",
|
||||
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "be356d05ecccd68043258d87b1892805",
|
||||
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "bf7d793d62416db7273590a796001cb6",
|
||||
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "b309e0cc3ec04024712c4ca938efdb92",
|
||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "bf7d793d62416db7273590a796001cb6",
|
||||
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "b309e0cc3ec04024712c4ca938efdb92",
|
||||
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "a86b09c31abf0b5ec934ef28c8bd9fa3",
|
||||
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "87be7a2f6e83c495f99024bb68660e17",
|
||||
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "a86b09c31abf0b5ec934ef28c8bd9fa3",
|
||||
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "87be7a2f6e83c495f99024bb68660e17",
|
||||
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "9fb5d3cb36dd53bd18c7ca831e7c73ee",
|
||||
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "88332859e6e9ee70848f5252e5ee6ce0",
|
||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "55b6db8700acfc573cc3db31c6b210f7",
|
||||
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "88332859e6e9ee70848f5252e5ee6ce0",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "cf40ba3bce2391e82978b08785405a5e",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "a8a74156e04932a2a5cc6d2d4b202acf",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "a135f9210a1c3be6b5d5d8228c8f6184",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "e412e20e4a0ac33b9f83c7750cde7109",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "cfcae11dab1c6752f821f0816706fa47",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "16daa37287a6d9d3404461da8565aadb",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "b3faf8b8925145f121b09e67d6114fb8",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "ed7ec02978df94f92168c5990cb6c78c",
|
||||
"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": "9f71f171464dc004dbaab87e9bb4b03b",
|
||||
"src/ballistica/base/mgen/pyembed/binding_base_app.inc": "a521bc86a7e98e56fec14cea029996f8",
|
||||
"src/assets/ba_data/python/babase/_mgen/enums.py": "28323912b56ec07701eda3d41a6a4101",
|
||||
"src/ballistica/base/mgen/pyembed/binding_base.inc": "ba8ce3ca3858b4c2d20db68f99b788b2",
|
||||
"src/ballistica/base/mgen/pyembed/binding_base_app.inc": "00f81f9bd92386ec12a6e60170678a98",
|
||||
"src/ballistica/classic/mgen/pyembed/binding_classic.inc": "3ceb412513963f0818ab39c58bf292e3",
|
||||
"src/ballistica/core/mgen/pyembed/binding_core.inc": "9d0a3c9636138e35284923e0c8311c69",
|
||||
"src/ballistica/core/mgen/pyembed/env.inc": "8be46e5818f360d10b7b0224a9e91d07",
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -120,6 +120,7 @@ xcuserdata/
|
||||
/ballisticakit-android/BallisticaKit/src/main/res/mipmap-*/ic_launcher*.png
|
||||
/ballisticakit-android/BallisticaKit/src/cardboard/res/mipmap-*/ic_launcher*.png
|
||||
BallisticaKit.ico
|
||||
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/Cursor macOS.appiconset/cursor_*.png
|
||||
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/AppIcon iOS.appiconset/icon_*.png
|
||||
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/AppIcon macOS.appiconset/icon_*.png
|
||||
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/tvOS App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Layer*.imagestacklayer/Content.imageset/*.png
|
||||
|
||||
2
.idea/dictionaries/ericf.xml
generated
2
.idea/dictionaries/ericf.xml
generated
@ -3192,6 +3192,8 @@
|
||||
<w>unstripped</w>
|
||||
<w>unstrl</w>
|
||||
<w>unsubscriptable</w>
|
||||
<w>unsuspend</w>
|
||||
<w>unsuspending</w>
|
||||
<w>untracked</w>
|
||||
<w>unwritable</w>
|
||||
<w>upcase</w>
|
||||
|
||||
28
CHANGELOG.md
28
CHANGELOG.md
@ -1,4 +1,4 @@
|
||||
### 1.7.28 (build 21422, api 8, 2023-10-05)
|
||||
### 1.7.28 (build 21443, api 8, 2023-10-11)
|
||||
|
||||
- Massively cleaned up code related to rendering and window systems (OpenGL,
|
||||
SDL, etc). This code had been growing into a nasty tangle for 15 years
|
||||
@ -104,6 +104,32 @@
|
||||
- Created a custom icon for BallisticaKit (previously it was just the BombSquad
|
||||
icon with an ugly 'C' on it). BombSquad itself will still have the BombSquad
|
||||
icon.
|
||||
- Changed `AppState.NOT_RUNNING` to `AppState.NOT_STARTED` since not-running
|
||||
could be confused with a state such as paused.
|
||||
- Changed the general app-state terms 'pause' and 'resume' to 'suspend' and
|
||||
'unsuspend'. (note this has nothing to do with pausing in the game which is
|
||||
still called pausing). The suspend state is used by mobile versions when
|
||||
backgrounded and basically stops all activity in the app. I may later add
|
||||
another state called 'paused' for when the app is still running but there is
|
||||
an OS dialog or ad or something in front of it. Though perhaps another term
|
||||
would be better to avoid confusion with the act of pausing in the game
|
||||
('inactive' maybe?).
|
||||
- Fixed an issue that could cause a few seconds delay when shutting down if
|
||||
internet access is unavailable.
|
||||
- Generalized the UI system to accept a delegate object, of which UIV1 is now
|
||||
one. In the future this will allow plugging in UIV2 instead or other UI
|
||||
systems.
|
||||
- Headless builds now plug in *no* ui delegate instead of UIV1, so one must
|
||||
avoid calling UI code from servers now. This should reduce server resource
|
||||
usage a bit. Please holler if this causes non-trivial problems. In general,
|
||||
code that brings up UI from gameplay contexts should check the value of
|
||||
`ba.app.env.headless` and avoid doing so when that is True.
|
||||
- Cleaned up quit behavior a bit more. The `babase.quit()` call now takes a
|
||||
single `babase.QuitType` enum instead of the multiple bool options it took
|
||||
before. It also takes a `confirm` bool arg which allows it to be used to bring
|
||||
up a confirm dialog.
|
||||
- Clicking on a window close button to quit no longer brings up a confirm dialog
|
||||
and instead quits immediately (though with a proper graceful shutdown).
|
||||
|
||||
### 1.7.27 (build 21282, api 8, 2023-08-30)
|
||||
|
||||
|
||||
2
ballisticakit-cmake/.idea/dictionaries/ericf.xml
generated
2
ballisticakit-cmake/.idea/dictionaries/ericf.xml
generated
@ -1876,6 +1876,8 @@
|
||||
<w>unpremultiply</w>
|
||||
<w>unsignaled</w>
|
||||
<w>unstuff</w>
|
||||
<w>unsuspend</w>
|
||||
<w>unsuspending</w>
|
||||
<w>unsynchronized</w>
|
||||
<w>unwritable</w>
|
||||
<w>uppercased</w>
|
||||
|
||||
@ -442,11 +442,11 @@ set(BALLISTICA_SOURCES
|
||||
${BA_SRC_ROOT}/ballistica/base/support/stdio_console.h
|
||||
${BA_SRC_ROOT}/ballistica/base/support/stress_test.cc
|
||||
${BA_SRC_ROOT}/ballistica/base/support/stress_test.h
|
||||
${BA_SRC_ROOT}/ballistica/base/support/ui_v1_soft.h
|
||||
${BA_SRC_ROOT}/ballistica/base/ui/dev_console.cc
|
||||
${BA_SRC_ROOT}/ballistica/base/ui/dev_console.h
|
||||
${BA_SRC_ROOT}/ballistica/base/ui/ui.cc
|
||||
${BA_SRC_ROOT}/ballistica/base/ui/ui.h
|
||||
${BA_SRC_ROOT}/ballistica/base/ui/ui_delegate.h
|
||||
${BA_SRC_ROOT}/ballistica/base/ui/widget_message.h
|
||||
${BA_SRC_ROOT}/ballistica/classic/classic.cc
|
||||
${BA_SRC_ROOT}/ballistica/classic/classic.h
|
||||
|
||||
@ -148,7 +148,7 @@
|
||||
<ExceptionHandling>SyncCThrow</ExceptionHandling>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<AdditionalIncludeDirectories>../../src;../../src/external/windows/include/SDL2;../../src/external/windows/include/AL;../../src/external/windows/include/python;../../src/external/windows/include;../../src/external/open_dynamics_engine-ef</AdditionalIncludeDirectories>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
@ -173,7 +173,7 @@
|
||||
<ExceptionHandling>SyncCThrow</ExceptionHandling>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<AdditionalIncludeDirectories>../../src;../../src/external/windows/include/SDL2;../../src/external/windows/include/AL;../../src/external/windows/include/python;../../src/external/windows/include;../../src/external/open_dynamics_engine-ef</AdditionalIncludeDirectories>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
@ -434,11 +434,11 @@
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\stdio_console.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\stress_test.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\stress_test.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\ui_v1_soft.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\ui\dev_console.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\ui\dev_console.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\ui\ui.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\ui\ui.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\ui\ui_delegate.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\ui\widget_message.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\classic\classic.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\classic\classic.h" />
|
||||
|
||||
@ -736,9 +736,6 @@
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\stress_test.h">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\ui_v1_soft.h">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\ui\dev_console.cc">
|
||||
<Filter>ballistica\base\ui</Filter>
|
||||
</ClCompile>
|
||||
@ -751,6 +748,9 @@
|
||||
<ClInclude Include="..\..\src\ballistica\base\ui\ui.h">
|
||||
<Filter>ballistica\base\ui</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ballistica\base\ui\ui_delegate.h">
|
||||
<Filter>ballistica\base\ui</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ballistica\base\ui\widget_message.h">
|
||||
<Filter>ballistica\base\ui</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@ -145,7 +145,7 @@
|
||||
<ForcedIncludeFiles>stdafx.h</ForcedIncludeFiles>
|
||||
<ExceptionHandling>SyncCThrow</ExceptionHandling>
|
||||
<AdditionalIncludeDirectories>../../src;../../src/external/windows/include/SDL2;../../src/external/windows/include/AL;../../src/external/windows/include/python;../../src/external/windows/include;../../src/external/open_dynamics_engine-ef</AdditionalIncludeDirectories>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
@ -169,7 +169,7 @@
|
||||
<ForcedIncludeFiles>stdafx.h</ForcedIncludeFiles>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<AdditionalIncludeDirectories>../../src;../../src/external/windows/include/SDL2;../../src/external/windows/include/AL;../../src/external/windows/include/python;../../src/external/windows/include;../../src/external/open_dynamics_engine-ef</AdditionalIncludeDirectories>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
@ -429,11 +429,11 @@
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\stdio_console.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\support\stress_test.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\stress_test.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\ui_v1_soft.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\ui\dev_console.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\ui\dev_console.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\base\ui\ui.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\ui\ui.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\ui\ui_delegate.h" />
|
||||
<ClInclude Include="..\..\src\ballistica\base\ui\widget_message.h" />
|
||||
<ClCompile Include="..\..\src\ballistica\classic\classic.cc" />
|
||||
<ClInclude Include="..\..\src\ballistica\classic\classic.h" />
|
||||
|
||||
@ -736,9 +736,6 @@
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\stress_test.h">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ballistica\base\support\ui_v1_soft.h">
|
||||
<Filter>ballistica\base\support</Filter>
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ballistica\base\ui\dev_console.cc">
|
||||
<Filter>ballistica\base\ui</Filter>
|
||||
</ClCompile>
|
||||
@ -751,6 +748,9 @@
|
||||
<ClInclude Include="..\..\src\ballistica\base\ui\ui.h">
|
||||
<Filter>ballistica\base\ui</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ballistica\base\ui\ui_delegate.h">
|
||||
<Filter>ballistica\base\ui</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ballistica\base\ui\widget_message.h">
|
||||
<Filter>ballistica\base\ui</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@ -162,6 +162,7 @@ from babase._mgen.enums import (
|
||||
SpecialChar,
|
||||
InputType,
|
||||
UIScale,
|
||||
QuitType,
|
||||
)
|
||||
from babase._math import normalized_color, is_point_in_box, vec3validate
|
||||
from babase._meta import MetadataSubsystem
|
||||
@ -286,6 +287,7 @@ __all__ = [
|
||||
'print_load_info',
|
||||
'pushcall',
|
||||
'quit',
|
||||
'QuitType',
|
||||
'reload_media',
|
||||
'request_permission',
|
||||
'safecolor',
|
||||
|
||||
@ -70,7 +70,7 @@ class App:
|
||||
|
||||
# The app has not yet begun starting and should not be used in
|
||||
# any way.
|
||||
NOT_RUNNING = 0
|
||||
NOT_STARTED = 0
|
||||
|
||||
# The native layer is spinning up its machinery (screens,
|
||||
# renderers, etc.). Nothing should happen in the Python layer
|
||||
@ -90,13 +90,23 @@ class App:
|
||||
# All pieces are in place and the app is now doing its thing.
|
||||
RUNNING = 4
|
||||
|
||||
# The app is backgrounded or otherwise suspended.
|
||||
PAUSED = 5
|
||||
# Used on platforms such as mobile where the app basically needs
|
||||
# to shut down while backgrounded. In this state, all event
|
||||
# loops are suspended and all graphics and audio should cease
|
||||
# completely. Be aware that the suspended state can be entered
|
||||
# from any other state including NATIVE_BOOTSTRAPPING and
|
||||
# SHUTTING_DOWN.
|
||||
SUSPENDED = 5
|
||||
|
||||
# The app is shutting down.
|
||||
# The app is shutting down. This process may involve sending
|
||||
# network messages or other things that can take up to a few
|
||||
# seconds, so ideally graphics and audio should remain
|
||||
# functional (with fades or spinners or whatever to show
|
||||
# something is happening).
|
||||
SHUTTING_DOWN = 6
|
||||
|
||||
# The app has completed shutdown.
|
||||
# The app has completed shutdown. Any code running here should
|
||||
# be basically immediate.
|
||||
SHUTDOWN_COMPLETE = 7
|
||||
|
||||
class DefaultAppModeSelector(AppModeSelector):
|
||||
@ -150,7 +160,7 @@ class App:
|
||||
return
|
||||
|
||||
self.env: babase.Env = _babase.Env()
|
||||
self.state = self.State.NOT_RUNNING
|
||||
self.state = self.State.NOT_STARTED
|
||||
|
||||
# Default executor which can be used for misc background
|
||||
# processing. It should also be passed to any additional asyncio
|
||||
@ -179,7 +189,7 @@ class App:
|
||||
self._init_completed = False
|
||||
self._meta_scan_completed = False
|
||||
self._native_start_called = False
|
||||
self._native_paused = False
|
||||
self._native_suspended = False
|
||||
self._native_shutdown_called = False
|
||||
self._native_shutdown_complete_called = False
|
||||
self._initial_sign_in_completed = False
|
||||
@ -197,7 +207,8 @@ class App:
|
||||
self._mode_selector: babase.AppModeSelector | None = None
|
||||
self._shutdown_task: asyncio.Task[None] | None = None
|
||||
self._shutdown_tasks: list[Coroutine[None, None, None]] = [
|
||||
self._wait_for_shutdown_suppressions()
|
||||
self._wait_for_shutdown_suppressions(),
|
||||
self._fade_for_shutdown(),
|
||||
]
|
||||
self._pool_thread_count = 0
|
||||
|
||||
@ -315,7 +326,7 @@ class App:
|
||||
def add_shutdown_task(self, coro: Coroutine[None, None, None]) -> None:
|
||||
"""Add a task to be run on app shutdown.
|
||||
|
||||
Note that tasks will be killed after
|
||||
Note that shutdown tasks will be canceled after
|
||||
App.SHUTDOWN_TASK_TIMEOUT_SECONDS if they are still running.
|
||||
"""
|
||||
if (
|
||||
@ -389,18 +400,18 @@ class App:
|
||||
self._native_bootstrapping_completed = True
|
||||
self._update_state()
|
||||
|
||||
def on_native_pause(self) -> None:
|
||||
"""Called by the native layer when the app pauses."""
|
||||
def on_native_suspend(self) -> None:
|
||||
"""Called by the native layer when the app is suspended."""
|
||||
assert _babase.in_logic_thread()
|
||||
assert not self._native_paused # Should avoid redundant calls.
|
||||
self._native_paused = True
|
||||
assert not self._native_suspended # Should avoid redundant calls.
|
||||
self._native_suspended = True
|
||||
self._update_state()
|
||||
|
||||
def on_native_resume(self) -> None:
|
||||
"""Called by the native layer when the app resumes."""
|
||||
def on_native_unsuspend(self) -> None:
|
||||
"""Called by the native layer when the app suspension ends."""
|
||||
assert _babase.in_logic_thread()
|
||||
assert self._native_paused # Should avoid redundant calls.
|
||||
self._native_paused = False
|
||||
assert self._native_suspended # Should avoid redundant calls.
|
||||
self._native_suspended = False
|
||||
self._update_state()
|
||||
|
||||
def on_native_shutdown(self) -> None:
|
||||
@ -730,15 +741,15 @@ class App:
|
||||
_babase.lifecyclelog('app state shutting down')
|
||||
self._on_shutting_down()
|
||||
|
||||
elif self._native_paused:
|
||||
# Entering paused state:
|
||||
if self.state is not self.State.PAUSED:
|
||||
self.state = self.State.PAUSED
|
||||
self._on_pause()
|
||||
elif self._native_suspended:
|
||||
# Entering suspended state:
|
||||
if self.state is not self.State.SUSPENDED:
|
||||
self.state = self.State.SUSPENDED
|
||||
self._on_suspend()
|
||||
else:
|
||||
# Leaving paused state:
|
||||
if self.state is self.State.PAUSED:
|
||||
self._on_resume()
|
||||
# Leaving suspended state:
|
||||
if self.state is self.State.SUSPENDED:
|
||||
self._on_unsuspend()
|
||||
|
||||
# Entering or returning to running state
|
||||
if self._initial_sign_in_completed and self._meta_scan_completed:
|
||||
@ -772,7 +783,7 @@ class App:
|
||||
self.state = self.State.NATIVE_BOOTSTRAPPING
|
||||
_babase.lifecyclelog('app state native bootstrapping')
|
||||
else:
|
||||
# Only logical possibility left is NOT_RUNNING, in which
|
||||
# Only logical possibility left is NOT_STARTED, in which
|
||||
# case we should not be getting called.
|
||||
logging.warning(
|
||||
'App._update_state called while in %s state;'
|
||||
@ -813,33 +824,33 @@ class App:
|
||||
try:
|
||||
await asyncio.wait_for(task, self.SHUTDOWN_TASK_TIMEOUT_SECONDS)
|
||||
except Exception:
|
||||
logging.exception('Error in shutdown task.')
|
||||
logging.exception('Error in shutdown task (%s).', coro)
|
||||
|
||||
def _on_pause(self) -> None:
|
||||
"""Called when the app goes to a paused state."""
|
||||
def _on_suspend(self) -> None:
|
||||
"""Called when the app goes to a suspended state."""
|
||||
assert _babase.in_logic_thread()
|
||||
|
||||
# Pause all app subsystems in the opposite order they were inited.
|
||||
# Suspend all app subsystems in the opposite order they were inited.
|
||||
for subsystem in reversed(self._subsystems):
|
||||
try:
|
||||
subsystem.on_app_pause()
|
||||
subsystem.on_app_suspend()
|
||||
except Exception:
|
||||
logging.exception(
|
||||
'Error in on_app_pause for subsystem %s.', subsystem
|
||||
'Error in on_app_suspend for subsystem %s.', subsystem
|
||||
)
|
||||
|
||||
def _on_resume(self) -> None:
|
||||
"""Called when resuming."""
|
||||
def _on_unsuspend(self) -> None:
|
||||
"""Called when unsuspending."""
|
||||
assert _babase.in_logic_thread()
|
||||
self.fg_state += 1
|
||||
|
||||
# Resume all app subsystems in the same order they were inited.
|
||||
# Unsuspend all app subsystems in the same order they were inited.
|
||||
for subsystem in self._subsystems:
|
||||
try:
|
||||
subsystem.on_app_resume()
|
||||
subsystem.on_app_unsuspend()
|
||||
except Exception:
|
||||
logging.exception(
|
||||
'Error in on_app_resume for subsystem %s.', subsystem
|
||||
'Error in on_app_unsuspend for subsystem %s.', subsystem
|
||||
)
|
||||
|
||||
def _on_shutting_down(self) -> None:
|
||||
@ -884,6 +895,19 @@ class App:
|
||||
await asyncio.sleep(0.001)
|
||||
_babase.lifecyclelog('shutdown-suppress wait end')
|
||||
|
||||
async def _fade_for_shutdown(self) -> None:
|
||||
import asyncio
|
||||
|
||||
# Kick off a fade, block input, and wait for a short bit.
|
||||
# Ideally most shutdown activity completes during the fade so
|
||||
# there's no tangible wait.
|
||||
_babase.lifecyclelog('fade-for-shutdown begin')
|
||||
_babase.fade_screen(False, time=0.15)
|
||||
_babase.lock_all_input()
|
||||
# _babase.getsimplesound('swish2').play()
|
||||
await asyncio.sleep(0.15)
|
||||
_babase.lifecyclelog('fade-for-shutdown end')
|
||||
|
||||
def _threadpool_no_wait_done(self, fut: Future) -> None:
|
||||
try:
|
||||
fut.result()
|
||||
|
||||
@ -39,10 +39,10 @@ class AppSubsystem:
|
||||
def on_app_running(self) -> None:
|
||||
"""Called when the app reaches the running state."""
|
||||
|
||||
def on_app_pause(self) -> None:
|
||||
def on_app_suspend(self) -> None:
|
||||
"""Called when the app enters the paused state."""
|
||||
|
||||
def on_app_resume(self) -> None:
|
||||
def on_app_unsuspend(self) -> None:
|
||||
"""Called when the app exits the paused state."""
|
||||
|
||||
def on_app_shutdown(self) -> None:
|
||||
|
||||
@ -64,7 +64,9 @@ def get_remote_app_name() -> babase.Lstr:
|
||||
|
||||
def should_submit_debug_info() -> bool:
|
||||
"""(internal)"""
|
||||
return _babase.app.config.get('Submit Debug Info', True)
|
||||
val = _babase.app.config.get('Submit Debug Info', True)
|
||||
assert isinstance(val, bool)
|
||||
return val
|
||||
|
||||
|
||||
def handle_v1_cloud_log() -> None:
|
||||
@ -442,10 +444,10 @@ class AppHealthMonitor(AppSubsystem):
|
||||
|
||||
self._first_check = False
|
||||
|
||||
def on_app_pause(self) -> None:
|
||||
def on_app_suspend(self) -> None:
|
||||
assert _babase.in_logic_thread()
|
||||
self._running = False
|
||||
|
||||
def on_app_resume(self) -> None:
|
||||
def on_app_unsuspend(self) -> None:
|
||||
assert _babase.in_logic_thread()
|
||||
self._running = True
|
||||
|
||||
@ -170,23 +170,23 @@ class PluginSubsystem(AppSubsystem):
|
||||
|
||||
_error.print_exception('Error in plugin on_app_running()')
|
||||
|
||||
def on_app_pause(self) -> None:
|
||||
def on_app_suspend(self) -> None:
|
||||
for plugin in self.active_plugins:
|
||||
try:
|
||||
plugin.on_app_pause()
|
||||
plugin.on_app_suspend()
|
||||
except Exception:
|
||||
from babase import _error
|
||||
|
||||
_error.print_exception('Error in plugin on_app_pause()')
|
||||
_error.print_exception('Error in plugin on_app_suspend()')
|
||||
|
||||
def on_app_resume(self) -> None:
|
||||
def on_app_unsuspend(self) -> None:
|
||||
for plugin in self.active_plugins:
|
||||
try:
|
||||
plugin.on_app_resume()
|
||||
plugin.on_app_unsuspend()
|
||||
except Exception:
|
||||
from babase import _error
|
||||
|
||||
_error.print_exception('Error in plugin on_app_resume()')
|
||||
_error.print_exception('Error in plugin on_app_unsuspend()')
|
||||
|
||||
def on_app_shutdown(self) -> None:
|
||||
for plugin in self.active_plugins:
|
||||
@ -327,11 +327,11 @@ class Plugin:
|
||||
def on_app_running(self) -> None:
|
||||
"""Called when the app reaches the running state."""
|
||||
|
||||
def on_app_pause(self) -> None:
|
||||
"""Called when the app is switching to a paused state."""
|
||||
def on_app_suspend(self) -> None:
|
||||
"""Called when the app enters the suspended state."""
|
||||
|
||||
def on_app_resume(self) -> None:
|
||||
"""Called when the app is resuming from a paused state."""
|
||||
def on_app_unsuspend(self) -> None:
|
||||
"""Called when the app exits the suspended state."""
|
||||
|
||||
def on_app_shutdown(self) -> None:
|
||||
"""Called when the app is beginning the shutdown process."""
|
||||
|
||||
@ -49,10 +49,10 @@ class AccountV1Subsystem:
|
||||
|
||||
babase.pushcall(do_auto_sign_in)
|
||||
|
||||
def on_app_pause(self) -> None:
|
||||
def on_app_suspend(self) -> None:
|
||||
"""Should be called when app is pausing."""
|
||||
|
||||
def on_app_resume(self) -> None:
|
||||
def on_app_unsuspend(self) -> None:
|
||||
"""Should be called when the app is resumed."""
|
||||
|
||||
# Mark our cached tourneys as invalid so anyone using them knows
|
||||
|
||||
@ -239,7 +239,7 @@ class MusicSubsystem:
|
||||
logging.exception('Error in get_soundtrack_entry_name.')
|
||||
return 'default'
|
||||
|
||||
def on_app_resume(self) -> None:
|
||||
def on_app_unsuspend(self) -> None:
|
||||
"""Should be run when the app resumes from a suspended state."""
|
||||
if babase.is_os_playing_music():
|
||||
self.do_play_music(None)
|
||||
|
||||
@ -229,12 +229,12 @@ class ClassicSubsystem(babase.AppSubsystem):
|
||||
|
||||
self.accounts.on_app_loading()
|
||||
|
||||
def on_app_pause(self) -> None:
|
||||
self.accounts.on_app_pause()
|
||||
def on_app_suspend(self) -> None:
|
||||
self.accounts.on_app_suspend()
|
||||
|
||||
def on_app_resume(self) -> None:
|
||||
self.accounts.on_app_resume()
|
||||
self.music.on_app_resume()
|
||||
def on_app_unsuspend(self) -> None:
|
||||
self.accounts.on_app_unsuspend()
|
||||
self.music.on_app_unsuspend()
|
||||
|
||||
def on_app_shutdown(self) -> None:
|
||||
self.music.on_app_shutdown()
|
||||
@ -701,11 +701,11 @@ class ClassicSubsystem(babase.AppSubsystem):
|
||||
|
||||
ShowURLWindow(address)
|
||||
|
||||
def quit_window(self) -> None:
|
||||
def quit_window(self, quit_type: babase.QuitType) -> None:
|
||||
"""(internal)"""
|
||||
from bauiv1lib.confirm import QuitWindow
|
||||
|
||||
QuitWindow()
|
||||
QuitWindow(quit_type)
|
||||
|
||||
def tournament_entry_window(
|
||||
self,
|
||||
|
||||
@ -52,7 +52,7 @@ if TYPE_CHECKING:
|
||||
|
||||
# Build number and version of the ballistica binary we expect to be
|
||||
# using.
|
||||
TARGET_BALLISTICA_BUILD = 21422
|
||||
TARGET_BALLISTICA_BUILD = 21443
|
||||
TARGET_BALLISTICA_VERSION = '1.7.28'
|
||||
|
||||
|
||||
|
||||
@ -87,7 +87,9 @@ class Campaign:
|
||||
|
||||
def get_selected_level(self) -> str:
|
||||
"""Return the name of the Level currently selected in the UI."""
|
||||
return self.configdict.get('Selection', self._levels[0].name)
|
||||
val = self.configdict.get('Selection', self._levels[0].name)
|
||||
assert isinstance(val, str)
|
||||
return val
|
||||
|
||||
@property
|
||||
def configdict(self) -> dict[str, Any]:
|
||||
|
||||
@ -105,7 +105,9 @@ class Level:
|
||||
def complete(self) -> bool:
|
||||
"""Whether this Level has been completed."""
|
||||
config = self._get_config_dict()
|
||||
return config.get('Complete', False)
|
||||
val = config.get('Complete', False)
|
||||
assert isinstance(val, bool)
|
||||
return val
|
||||
|
||||
def set_complete(self, val: bool) -> None:
|
||||
"""Set whether or not this level is complete."""
|
||||
@ -147,7 +149,9 @@ class Level:
|
||||
@property
|
||||
def rating(self) -> float:
|
||||
"""The current rating for this Level."""
|
||||
return self._get_config_dict().get('Rating', 0.0)
|
||||
val = self._get_config_dict().get('Rating', 0.0)
|
||||
assert isinstance(val, float)
|
||||
return val
|
||||
|
||||
def set_rating(self, rating: float) -> None:
|
||||
"""Set a rating for this Level, replacing the old ONLY IF higher."""
|
||||
|
||||
@ -162,8 +162,11 @@ class MultiTeamSession(Session):
|
||||
def get_max_players(self) -> int:
|
||||
"""Return max number of Players allowed to join the game at once."""
|
||||
if self.use_teams:
|
||||
return babase.app.config.get('Team Game Max Players', 8)
|
||||
return babase.app.config.get('Free-for-All Max Players', 8)
|
||||
val = babase.app.config.get('Team Game Max Players', 8)
|
||||
else:
|
||||
val = babase.app.config.get('Free-for-All Max Players', 8)
|
||||
assert isinstance(val, int)
|
||||
return val
|
||||
|
||||
def _instantiate_next_game(self) -> None:
|
||||
self._next_game_instance = _bascenev1.newactivity(
|
||||
|
||||
@ -14,13 +14,12 @@ from bascenev1lib.actor.flag import Flag
|
||||
from bascenev1lib.actor.scoreboard import Scoreboard
|
||||
from bascenev1lib.actor.playerspaz import PlayerSpaz
|
||||
from bascenev1lib.gameutils import SharedObjects
|
||||
from bascenev1lib.actor.respawnicon import RespawnIcon
|
||||
import bascenev1 as bs
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Any, Sequence
|
||||
|
||||
from bascenev1lib.actor.respawnicon import RespawnIcon
|
||||
|
||||
|
||||
class ConquestFlag(Flag):
|
||||
"""A custom flag for use with Conquest games."""
|
||||
@ -49,7 +48,9 @@ class Player(bs.Player['Team']):
|
||||
@property
|
||||
def respawn_timer(self) -> bs.Timer | None:
|
||||
"""Type safe access to standard respawn timer."""
|
||||
return self.customdata.get('respawn_timer', None)
|
||||
val = self.customdata.get('respawn_timer', None)
|
||||
assert isinstance(val, (bs.Timer, type(None)))
|
||||
return val
|
||||
|
||||
@respawn_timer.setter
|
||||
def respawn_timer(self, value: bs.Timer | None) -> None:
|
||||
@ -58,7 +59,9 @@ class Player(bs.Player['Team']):
|
||||
@property
|
||||
def respawn_icon(self) -> RespawnIcon | None:
|
||||
"""Type safe access to standard respawn icon."""
|
||||
return self.customdata.get('respawn_icon', None)
|
||||
val = self.customdata.get('respawn_icon', None)
|
||||
assert isinstance(val, (RespawnIcon, type(None)))
|
||||
return val
|
||||
|
||||
@respawn_icon.setter
|
||||
def respawn_icon(self, value: RespawnIcon | None) -> None:
|
||||
|
||||
@ -300,7 +300,10 @@ class MainMenuActivity(bs.Activity[bs.Player, bs.Team]):
|
||||
from bauiv1lib import specialoffer
|
||||
|
||||
assert bs.app.classic is not None
|
||||
if bool(False):
|
||||
if bui.app.env.headless:
|
||||
# UI stuff fails now in headless builds; avoid it.
|
||||
pass
|
||||
elif bool(False):
|
||||
uicontroller = bs.app.ui_v1.controller
|
||||
assert uicontroller is not None
|
||||
uicontroller.show_main_menu()
|
||||
|
||||
@ -69,6 +69,7 @@ from babase import (
|
||||
PluginSpec,
|
||||
pushcall,
|
||||
quit,
|
||||
QuitType,
|
||||
request_permission,
|
||||
safecolor,
|
||||
screenmessage,
|
||||
@ -192,6 +193,7 @@ __all__ = [
|
||||
'PluginSpec',
|
||||
'pushcall',
|
||||
'quit',
|
||||
'QuitType',
|
||||
'request_permission',
|
||||
'rowwidget',
|
||||
'safecolor',
|
||||
|
||||
@ -13,6 +13,8 @@ import _bauiv1
|
||||
if TYPE_CHECKING:
|
||||
from typing import Sequence
|
||||
|
||||
import babase
|
||||
|
||||
|
||||
def ticket_icon_press() -> None:
|
||||
from babase import app
|
||||
@ -57,14 +59,14 @@ def party_icon_activate(origin: Sequence[float]) -> None:
|
||||
logging.warning('party_icon_activate: no classic.')
|
||||
|
||||
|
||||
def quit_window() -> None:
|
||||
def quit_window(quit_type: babase.QuitType) -> None:
|
||||
from babase import app
|
||||
|
||||
if app.classic is None:
|
||||
logging.exception('Classic not present.')
|
||||
return
|
||||
|
||||
app.classic.quit_window()
|
||||
app.classic.quit_window(quit_type)
|
||||
|
||||
|
||||
def device_menu_press(device_id: int | None) -> None:
|
||||
|
||||
@ -153,15 +153,15 @@ class QuitWindow:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
quit_type: bui.QuitType | None = None,
|
||||
swish: bool = False,
|
||||
back: bool = False,
|
||||
origin_widget: bui.Widget | None = None,
|
||||
):
|
||||
classic = bui.app.classic
|
||||
assert classic is not None
|
||||
ui = bui.app.ui_v1
|
||||
app = bui.app
|
||||
self._back = back
|
||||
self._quit_type = quit_type
|
||||
|
||||
# If there's already one of us up somewhere, kill it.
|
||||
if ui.quit_window is not None:
|
||||
@ -187,29 +187,8 @@ class QuitWindow:
|
||||
resource=quit_resource,
|
||||
subs=[('${APP_NAME}', bui.Lstr(resource='titleText'))],
|
||||
),
|
||||
self._fade_and_quit,
|
||||
lambda: bui.quit(confirm=False, quit_type=self._quit_type)
|
||||
if self._quit_type is not None
|
||||
else bui.quit(confirm=False),
|
||||
origin_widget=origin_widget,
|
||||
).root_widget
|
||||
|
||||
def _fade_and_quit(self) -> None:
|
||||
bui.fade_screen(
|
||||
False,
|
||||
time=0.2,
|
||||
endcall=lambda: bui.quit(soft=True, back=self._back),
|
||||
)
|
||||
|
||||
# Prevent the user from doing anything else while we're on our
|
||||
# way out.
|
||||
bui.lock_all_input()
|
||||
|
||||
# On systems supporting soft-quit, unlock and fade back in shortly
|
||||
# (soft-quit basically just backgrounds/hides the app).
|
||||
if bui.app.env.supports_soft_quit:
|
||||
# Unlock and fade back in shortly. Just in case something goes
|
||||
# wrong (or on Android where quit just backs out of our activity
|
||||
# and we may come back after).
|
||||
def _come_back() -> None:
|
||||
bui.unlock_all_input()
|
||||
bui.fade_screen(True)
|
||||
|
||||
bui.apptimer(0.5, _come_back)
|
||||
|
||||
@ -21,7 +21,7 @@ class KioskWindow(bui.Window):
|
||||
self._height = 340.0
|
||||
|
||||
def _do_cancel() -> None:
|
||||
QuitWindow(swish=True, back=True)
|
||||
QuitWindow(swish=True, quit_type=bui.QuitType.BACK)
|
||||
|
||||
super().__init__(
|
||||
root_widget=bui.containerwidget(
|
||||
|
||||
@ -190,7 +190,6 @@ class MainMenuWindow(bui.Window):
|
||||
# pylint: disable=too-many-branches
|
||||
# pylint: disable=too-many-locals
|
||||
# pylint: disable=too-many-statements
|
||||
from bauiv1lib.confirm import QuitWindow
|
||||
from bauiv1lib.store.button import StoreButton
|
||||
|
||||
plus = bui.app.plus
|
||||
@ -422,7 +421,7 @@ class MainMenuWindow(bui.Window):
|
||||
):
|
||||
|
||||
def _do_quit() -> None:
|
||||
QuitWindow(swish=True, back=True)
|
||||
bui.quit(confirm=True, quit_type=bui.QuitType.BACK)
|
||||
|
||||
bui.containerwidget(
|
||||
edit=self._root_widget, on_cancel_call=_do_quit
|
||||
@ -1040,6 +1039,9 @@ class MainMenuWindow(bui.Window):
|
||||
# pylint: disable=cyclic-import
|
||||
from bauiv1lib.confirm import QuitWindow
|
||||
|
||||
# Note: Normally we should go through bui.quit(confirm=True) but
|
||||
# invoking the window directly lets us scale it up from the
|
||||
# button.
|
||||
QuitWindow(origin_widget=self._quit_button)
|
||||
|
||||
def _demo_menu_press(self) -> None:
|
||||
|
||||
@ -431,7 +431,9 @@ class GamepadSettingsWindow(bui.Window):
|
||||
def get_unassigned_buttons_run_value(self) -> bool:
|
||||
"""(internal)"""
|
||||
assert self._settings is not None
|
||||
return self._settings.get('unassignedButtonsRun', True)
|
||||
val = self._settings.get('unassignedButtonsRun', True)
|
||||
assert isinstance(val, bool)
|
||||
return val
|
||||
|
||||
def set_unassigned_buttons_run_value(self, value: bool) -> None:
|
||||
"""(internal)"""
|
||||
@ -446,7 +448,9 @@ class GamepadSettingsWindow(bui.Window):
|
||||
def get_start_button_activates_default_widget_value(self) -> bool:
|
||||
"""(internal)"""
|
||||
assert self._settings is not None
|
||||
return self._settings.get('startButtonActivatesDefaultWidget', True)
|
||||
val = self._settings.get('startButtonActivatesDefaultWidget', True)
|
||||
assert isinstance(val, bool)
|
||||
return val
|
||||
|
||||
def set_start_button_activates_default_widget_value(
|
||||
self, value: bool
|
||||
@ -463,7 +467,9 @@ class GamepadSettingsWindow(bui.Window):
|
||||
def get_ui_only_value(self) -> bool:
|
||||
"""(internal)"""
|
||||
assert self._settings is not None
|
||||
return self._settings.get('uiOnly', False)
|
||||
val = self._settings.get('uiOnly', False)
|
||||
assert isinstance(val, bool)
|
||||
return val
|
||||
|
||||
def set_ui_only_value(self, value: bool) -> None:
|
||||
"""(internal)"""
|
||||
@ -478,7 +484,9 @@ class GamepadSettingsWindow(bui.Window):
|
||||
def get_ignore_completely_value(self) -> bool:
|
||||
"""(internal)"""
|
||||
assert self._settings is not None
|
||||
return self._settings.get('ignoreCompletely', False)
|
||||
val = self._settings.get('ignoreCompletely', False)
|
||||
assert isinstance(val, bool)
|
||||
return val
|
||||
|
||||
def set_ignore_completely_value(self, value: bool) -> None:
|
||||
"""(internal)"""
|
||||
@ -493,7 +501,9 @@ class GamepadSettingsWindow(bui.Window):
|
||||
def get_auto_recalibrate_analog_stick_value(self) -> bool:
|
||||
"""(internal)"""
|
||||
assert self._settings is not None
|
||||
return self._settings.get('autoRecalibrateAnalogStick', False)
|
||||
val = self._settings.get('autoRecalibrateAnalogStick', False)
|
||||
assert isinstance(val, bool)
|
||||
return val
|
||||
|
||||
def set_auto_recalibrate_analog_stick_value(self, value: bool) -> None:
|
||||
"""(internal)"""
|
||||
@ -510,7 +520,9 @@ class GamepadSettingsWindow(bui.Window):
|
||||
assert self._settings is not None
|
||||
if not self._is_secondary:
|
||||
raise RuntimeError('Enable value only applies to secondary editor.')
|
||||
return self._settings.get('enableSecondary', False)
|
||||
val = self._settings.get('enableSecondary', False)
|
||||
assert isinstance(val, bool)
|
||||
return val
|
||||
|
||||
def show_secondary_editor(self) -> None:
|
||||
"""(internal)"""
|
||||
|
||||
@ -1411,6 +1411,6 @@ def _check_merch_availability_in_bg_thread() -> None:
|
||||
# to do this during docs generation/etc.)
|
||||
if (
|
||||
os.environ.get('BA_RUNNING_WITH_DUMMY_MODULES') != '1'
|
||||
and bui.app.state is not bui.app.State.NOT_RUNNING
|
||||
and bui.app.state is not bui.app.State.NOT_STARTED
|
||||
):
|
||||
Thread(target=_check_merch_availability_in_bg_thread, daemon=True).start()
|
||||
|
||||
@ -76,7 +76,7 @@ void AppAdapter::OnMainThreadStartApp() {
|
||||
if (!g_core->HeadlessMode()) {
|
||||
// If we've got a nice themed hardware cursor, show it. Otherwise we'll
|
||||
// render it manually, which is laggier but gets the job done.
|
||||
g_base->platform->SetHardwareCursorVisible(g_buildconfig.hardware_cursor());
|
||||
// g_base->platform->SetHardwareCursorVisible(g_buildconfig.hardware_cursor());
|
||||
|
||||
// On desktop systems we just assume keyboard input exists and add it
|
||||
// immediately.
|
||||
@ -100,7 +100,7 @@ void AppAdapter::OnAppShutdownComplete() { assert(g_base->InLogicThread()); }
|
||||
void AppAdapter::OnScreenSizeChange() { assert(g_base->InLogicThread()); }
|
||||
void AppAdapter::DoApplyAppConfig() { assert(g_base->InLogicThread()); }
|
||||
|
||||
void AppAdapter::OnAppPause_() {
|
||||
void AppAdapter::OnAppSuspend_() {
|
||||
assert(g_core->InMainThread());
|
||||
|
||||
// IMPORTANT: Any pause related stuff that event-loop-threads need to do
|
||||
@ -109,7 +109,7 @@ void AppAdapter::OnAppPause_() {
|
||||
// their event-loop is actually paused.
|
||||
|
||||
// Pause all event loops.
|
||||
EventLoop::SetEventLoopsPaused(true);
|
||||
EventLoop::SetEventLoopsSuspended(true);
|
||||
|
||||
if (g_base->network_reader) {
|
||||
g_base->network_reader->OnAppPause();
|
||||
@ -117,24 +117,26 @@ void AppAdapter::OnAppPause_() {
|
||||
g_base->networking->OnAppPause();
|
||||
}
|
||||
|
||||
void AppAdapter::OnAppResume_() {
|
||||
void AppAdapter::OnAppUnsuspend_() {
|
||||
assert(g_core->InMainThread());
|
||||
|
||||
// Spin all event-loops back up.
|
||||
EventLoop::SetEventLoopsPaused(false);
|
||||
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 paused state, we may want to pause whatever game
|
||||
// was running when we last were active.
|
||||
// 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.
|
||||
if (g_core->should_pause) {
|
||||
g_core->should_pause = false;
|
||||
//
|
||||
// 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.
|
||||
@ -144,13 +146,13 @@ void AppAdapter::OnAppResume_() {
|
||||
}
|
||||
}
|
||||
|
||||
void AppAdapter::PauseApp() {
|
||||
void AppAdapter::SuspendApp() {
|
||||
assert(g_core);
|
||||
assert(g_core->InMainThread());
|
||||
|
||||
if (app_paused_) {
|
||||
if (app_suspended_) {
|
||||
Log(LogLevel::kWarning,
|
||||
"AppAdapter::PauseApp() called with app already paused.");
|
||||
"AppAdapter::SuspendApp() called with app already suspended.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -161,11 +163,12 @@ void AppAdapter::PauseApp() {
|
||||
millisecs_t max_duration{2000};
|
||||
|
||||
g_core->platform->DebugLog(
|
||||
"PauseApp@" + std::to_string(core::CorePlatform::GetCurrentMillisecs()));
|
||||
"SuspendApp@"
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs()));
|
||||
// assert(!app_pause_requested_);
|
||||
// app_pause_requested_ = true;
|
||||
app_paused_ = true;
|
||||
OnAppPause_();
|
||||
app_suspended_ = true;
|
||||
OnAppSuspend_();
|
||||
// UpdatePauseResume_();
|
||||
|
||||
// We assume that the OS will completely suspend our process the moment we
|
||||
@ -177,12 +180,12 @@ void AppAdapter::PauseApp() {
|
||||
< max_duration) {
|
||||
// If/when we get to a point with no threads waiting to be paused, we're
|
||||
// good to go.
|
||||
auto threads{EventLoop::GetStillPausingThreads()};
|
||||
auto threads{EventLoop::GetStillSuspendingEventLoops()};
|
||||
running_thread_count = threads.size();
|
||||
if (running_thread_count == 0) {
|
||||
if (g_buildconfig.debug_build()) {
|
||||
Log(LogLevel::kDebug,
|
||||
"PauseApp() completed in "
|
||||
"SuspendApp() completed in "
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs()
|
||||
- start_time)
|
||||
+ "ms.");
|
||||
@ -193,7 +196,7 @@ void AppAdapter::PauseApp() {
|
||||
|
||||
// If we made it here, we timed out. Complain.
|
||||
Log(LogLevel::kError,
|
||||
std::string("PauseApp() took too long; ")
|
||||
std::string("SuspendApp() took too long; ")
|
||||
+ std::to_string(running_thread_count)
|
||||
+ " threads not yet paused after "
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs()
|
||||
@ -201,26 +204,27 @@ void AppAdapter::PauseApp() {
|
||||
+ " ms.");
|
||||
}
|
||||
|
||||
void AppAdapter::ResumeApp() {
|
||||
void AppAdapter::UnsuspendApp() {
|
||||
assert(g_core);
|
||||
assert(g_core->InMainThread());
|
||||
|
||||
if (!app_paused_) {
|
||||
if (!app_suspended_) {
|
||||
Log(LogLevel::kWarning,
|
||||
"AppAdapter::ResumeApp() called with app not in paused state.");
|
||||
"AppAdapter::UnsuspendApp() called with app not in paused state.");
|
||||
return;
|
||||
}
|
||||
millisecs_t start_time{core::CorePlatform::GetCurrentMillisecs()};
|
||||
g_core->platform->DebugLog(
|
||||
"ResumeApp@" + std::to_string(core::CorePlatform::GetCurrentMillisecs()));
|
||||
"UnsuspendApp@"
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs()));
|
||||
// assert(app_pause_requested_);
|
||||
// app_pause_requested_ = false;
|
||||
// UpdatePauseResume_();
|
||||
app_paused_ = false;
|
||||
OnAppResume_();
|
||||
app_suspended_ = false;
|
||||
OnAppUnsuspend_();
|
||||
if (g_buildconfig.debug_build()) {
|
||||
Log(LogLevel::kDebug,
|
||||
"ResumeApp() completed in "
|
||||
"UnsuspendApp() completed in "
|
||||
+ std::to_string(core::CorePlatform::GetCurrentMillisecs()
|
||||
- start_time)
|
||||
+ "ms.");
|
||||
@ -249,4 +253,32 @@ void AppAdapter::DoPushGraphicsContextRunnable(Runnable* runnable) {
|
||||
DoPushMainThreadRunnable(runnable);
|
||||
}
|
||||
|
||||
void AppAdapter::CursorPositionForDraw(float* x, float* y) {
|
||||
assert(x && y);
|
||||
|
||||
// By default, just use our latest event-delivered cursor position;
|
||||
// this should work everywhere though perhaps might not be most optimal.
|
||||
if (g_base->input == nullptr) {
|
||||
*x = 0.0f;
|
||||
*y = 0.0f;
|
||||
return;
|
||||
}
|
||||
*x = g_base->input->cursor_pos_x();
|
||||
*y = g_base->input->cursor_pos_y();
|
||||
}
|
||||
|
||||
auto AppAdapter::ShouldUseCursor() -> bool { return true; }
|
||||
|
||||
auto AppAdapter::HasHardwareCursor() -> bool { return false; }
|
||||
|
||||
void AppAdapter::SetHardwareCursorVisible(bool visible) {
|
||||
printf("SHOULD SET VIS %d\n", static_cast<int>(visible));
|
||||
}
|
||||
|
||||
auto AppAdapter::CanSoftQuit() -> bool { return false; }
|
||||
auto AppAdapter::CanBackQuit() -> bool { return false; }
|
||||
void AppAdapter::DoBackQuit() { FatalError("Fixme unimplemented."); }
|
||||
void AppAdapter::DoSoftQuit() { FatalError("Fixme unimplemented."); }
|
||||
void AppAdapter::TerminateApp() { FatalError("Fixme unimplemented."); }
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
@ -55,30 +55,62 @@ class AppAdapter {
|
||||
|
||||
/// Should return whether the current thread and/or context setup is the
|
||||
/// one where graphics calls should be made. For the default
|
||||
/// implementation, this simply returns true in the main thread.
|
||||
/// implementation, this simply returns true in the main thread. Note
|
||||
/// that, while it is valid for the graphics context thread to change over
|
||||
/// time, no more than one thread at a time should ever be considered the
|
||||
/// graphics context.
|
||||
virtual auto InGraphicsContext() -> bool;
|
||||
|
||||
/// Push a call to be run in the app's graphics context. Be aware that
|
||||
/// this may mean different threads on different platforms.
|
||||
/// Push a call to be run in the app's graphics context. This may mean
|
||||
/// different things depending on the graphics architecture in use. On
|
||||
/// some platforms this simply pushes to the main thread. On others it may
|
||||
/// schedule a call to be run just before or after a frame draw in a
|
||||
/// dedicated render thread. The default implementation pushes to the main
|
||||
/// thread.
|
||||
template <typename F>
|
||||
void PushGraphicsContextCall(const F& lambda) {
|
||||
DoPushGraphicsContextRunnable(NewLambdaRunnableUnmanaged(lambda));
|
||||
}
|
||||
|
||||
/// Return whether the current setup should show a cursor for mouse
|
||||
/// motion. This generally should be true for desktop type situations or
|
||||
/// if a mouse is present on a mobile device and false for purely touch
|
||||
/// based situations. This value may change over time if a mouse is
|
||||
/// 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
|
||||
/// periodically to inform the adapter what the cursor state should be.
|
||||
/// The default implementation returns false;
|
||||
virtual auto HasHardwareCursor() -> bool;
|
||||
|
||||
/// If HasHardwareCursor() returns true, this will be called in the main
|
||||
/// thread periodically when the adapter should be hiding/showing the
|
||||
/// cursor.
|
||||
virtual void SetHardwareCursorVisible(bool visible);
|
||||
|
||||
/// Called to get the cursor position when drawing. Default implementation
|
||||
/// returns the latest position delivered through the input subsystem, but
|
||||
/// subclasses may want to override to provide slightly more up to date
|
||||
/// values.
|
||||
virtual void CursorPositionForDraw(float* x, float* y);
|
||||
|
||||
/// Put the app into a paused 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 PauseApp();
|
||||
void SuspendApp();
|
||||
|
||||
/// Resume the app; can correspond to foregrounding on mobile,
|
||||
/// unminimizing on desktop, etc. Spins threads back up, re-opens network
|
||||
/// sockets, etc.
|
||||
void ResumeApp();
|
||||
void UnsuspendApp();
|
||||
|
||||
auto app_paused() const { return app_paused_; }
|
||||
auto app_suspended() const { return app_suspended_; }
|
||||
|
||||
/// Return whether this AppAdapter supports a 'fullscreen' toggle for its
|
||||
/// display. This currently will simply affect whether that option is
|
||||
@ -91,6 +123,36 @@ class AppAdapter {
|
||||
/// Return whether this AppAdapter supports max-fps controls for its display.
|
||||
virtual auto SupportsMaxFPS() -> 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.
|
||||
virtual auto CanSoftQuit() -> bool;
|
||||
|
||||
/// Implement soft-quit behavior. Will always be called in the logic
|
||||
/// thread. Make sure to also override CanBackQuit to reflect this being
|
||||
/// present. Note that when quitting the app yourself, you should use
|
||||
/// g_base->QuitApp(); do not call this directly.
|
||||
virtual void DoSoftQuit();
|
||||
|
||||
/// Return whether this platform supports back-quit. A back quit is a
|
||||
/// variation of soft-quit generally triggered by a back button, which may
|
||||
/// give different results in the OS. For example on Android this may
|
||||
/// result in jumping back to the previous Android activity instead of
|
||||
/// just ending the current one and dumping to the home screen as normal
|
||||
/// soft quit might do.
|
||||
virtual auto CanBackQuit() -> bool;
|
||||
|
||||
/// Implement back-quit behavior. Will always be called in the logic
|
||||
/// thread. Make sure to also override CanBackQuit to reflect this being
|
||||
/// present. Note that when quitting the app yourself, you should use
|
||||
/// g_base->QuitApp(); do not call this directly.
|
||||
virtual void DoBackQuit();
|
||||
|
||||
/// Terminate the app. This can be immediate or by posting some high
|
||||
/// level event. There should be nothing left to do in the engine at
|
||||
/// this point.
|
||||
virtual void TerminateApp();
|
||||
|
||||
protected:
|
||||
AppAdapter();
|
||||
virtual ~AppAdapter();
|
||||
@ -104,9 +166,9 @@ class AppAdapter {
|
||||
virtual void DoPushGraphicsContextRunnable(Runnable* runnable);
|
||||
|
||||
private:
|
||||
void OnAppPause_();
|
||||
void OnAppResume_();
|
||||
bool app_paused_{};
|
||||
void OnAppSuspend_();
|
||||
void OnAppUnsuspend_();
|
||||
bool app_suspended_{};
|
||||
};
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
@ -5,10 +5,34 @@
|
||||
|
||||
#include <BallisticaKit-Swift.h>
|
||||
|
||||
#include "ballistica/base/graphics/gl/renderer_gl.h"
|
||||
#include "ballistica/base/graphics/graphics.h"
|
||||
#include "ballistica/base/graphics/graphics_server.h"
|
||||
#include "ballistica/base/logic/logic.h"
|
||||
#include "ballistica/base/support/app_config.h"
|
||||
#include "ballistica/shared/ballistica.h"
|
||||
#include "ballistica/shared/foundation/event_loop.h"
|
||||
|
||||
namespace ballistica::base {
|
||||
|
||||
/// RAII-friendly way to mark the thread and calls we're allowed to run graphics
|
||||
/// stuff in.
|
||||
class AppAdapterApple::ScopedAllowGraphics_ {
|
||||
public:
|
||||
explicit ScopedAllowGraphics_(AppAdapterApple* adapter) : adapter_{adapter} {
|
||||
assert(!adapter_->graphics_allowed_);
|
||||
adapter->graphics_thread_ = std::this_thread::get_id();
|
||||
adapter->graphics_allowed_ = true;
|
||||
}
|
||||
~ScopedAllowGraphics_() {
|
||||
assert(adapter_->graphics_allowed_);
|
||||
adapter_->graphics_allowed_ = false;
|
||||
}
|
||||
|
||||
private:
|
||||
AppAdapterApple* adapter_;
|
||||
};
|
||||
|
||||
auto AppAdapterApple::ManagesMainThreadEventLoop() const -> bool {
|
||||
// Nope; we run under a standard Cocoa/UIKit environment and they call us; we
|
||||
// don't call them.
|
||||
@ -17,7 +41,166 @@ auto AppAdapterApple::ManagesMainThreadEventLoop() const -> bool {
|
||||
|
||||
void AppAdapterApple::DoPushMainThreadRunnable(Runnable* runnable) {
|
||||
// Kick this along to swift.
|
||||
BallisticaKit::PushRawRunnableToMain(runnable);
|
||||
BallisticaKit::FromCppPushRawRunnableToMain(runnable);
|
||||
}
|
||||
|
||||
void AppAdapterApple::DoApplyAppConfig() {
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
g_base->graphics_server->PushSetScreenPixelScaleCall(
|
||||
g_base->app_config->Resolve(AppConfig::FloatID::kScreenPixelScale));
|
||||
|
||||
auto graphics_quality_requested =
|
||||
g_base->graphics->GraphicsQualityFromAppConfig();
|
||||
|
||||
auto texture_quality_requested =
|
||||
g_base->graphics->TextureQualityFromAppConfig();
|
||||
|
||||
g_base->app_adapter->PushGraphicsContextCall([=] {
|
||||
SetScreen_(texture_quality_requested, graphics_quality_requested);
|
||||
});
|
||||
}
|
||||
|
||||
void AppAdapterApple::SetScreen_(
|
||||
TextureQualityRequest texture_quality_requested,
|
||||
GraphicsQualityRequest graphics_quality_requested) {
|
||||
// If we know what we support, filter our request types to what is
|
||||
// supported. This will keep us from rebuilding contexts if request type
|
||||
// is flipping between different types that we don't support.
|
||||
if (g_base->graphics->has_supports_high_quality_graphics_value()) {
|
||||
if (!g_base->graphics->supports_high_quality_graphics()
|
||||
&& graphics_quality_requested > GraphicsQualityRequest::kMedium) {
|
||||
graphics_quality_requested = GraphicsQualityRequest::kMedium;
|
||||
}
|
||||
}
|
||||
|
||||
auto* gs = g_base->graphics_server;
|
||||
|
||||
// We need a full renderer reload if quality values have changed
|
||||
// or if we don't have one yet.
|
||||
bool need_full_reload =
|
||||
((gs->texture_quality_requested() != texture_quality_requested)
|
||||
|| (gs->graphics_quality_requested() != graphics_quality_requested)
|
||||
|| !gs->texture_quality_set() || !gs->graphics_quality_set());
|
||||
|
||||
if (need_full_reload) {
|
||||
ReloadRenderer_(graphics_quality_requested, texture_quality_requested);
|
||||
}
|
||||
|
||||
// Let the logic thread know we've got a graphics system up and running.
|
||||
// It may use this cue to kick off asset loads and other bootstrapping.
|
||||
g_base->logic->event_loop()->PushCall(
|
||||
[] { g_base->logic->OnGraphicsReady(); });
|
||||
}
|
||||
|
||||
void AppAdapterApple::ReloadRenderer_(
|
||||
GraphicsQualityRequest graphics_quality_requested,
|
||||
TextureQualityRequest texture_quality_requested) {
|
||||
auto* gs = g_base->graphics_server;
|
||||
|
||||
if (gs->renderer() && gs->renderer_loaded()) {
|
||||
gs->UnloadRenderer();
|
||||
}
|
||||
if (!gs->renderer()) {
|
||||
gs->set_renderer(new RendererGL());
|
||||
}
|
||||
|
||||
// Set a dummy screen resolution to start with.
|
||||
// The main thread will kick along the latest real resolution just before
|
||||
// each frame draw, but we need *something* here or else we'll get errors due
|
||||
// to framebuffers getting made at size 0/etc.
|
||||
g_base->graphics_server->SetScreenResolution(320.0, 240.0);
|
||||
|
||||
// Update graphics quality based on request.
|
||||
gs->set_graphics_quality_requested(graphics_quality_requested);
|
||||
gs->set_texture_quality_requested(texture_quality_requested);
|
||||
|
||||
// (Re)load stuff with these latest quality settings.
|
||||
gs->LoadRenderer();
|
||||
}
|
||||
|
||||
void AppAdapterApple::UpdateScreenSizes_() {
|
||||
assert(g_base->app_adapter->InGraphicsContext());
|
||||
}
|
||||
|
||||
void AppAdapterApple::SetScreenResolution(float pixel_width,
|
||||
float pixel_height) {
|
||||
auto allow = ScopedAllowGraphics_(this);
|
||||
g_base->graphics_server->SetScreenResolution(pixel_width, pixel_height);
|
||||
}
|
||||
|
||||
auto AppAdapterApple::TryRender() -> bool {
|
||||
auto allow = ScopedAllowGraphics_(this);
|
||||
|
||||
// Run & release any pending runnables.
|
||||
std::vector<Runnable*> calls;
|
||||
{
|
||||
// Pull calls off the list before running them; this way we only need
|
||||
// to grab the list lock for a moment.
|
||||
auto lock = std::scoped_lock(graphics_calls_mutex_);
|
||||
if (!graphics_calls_.empty()) {
|
||||
graphics_calls_.swap(calls);
|
||||
}
|
||||
}
|
||||
for (auto* call : calls) {
|
||||
call->RunAndLogErrors();
|
||||
delete call;
|
||||
}
|
||||
// Lastly render.
|
||||
return g_base->graphics_server->TryRender();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto AppAdapterApple::InGraphicsContext() -> bool {
|
||||
return std::this_thread::get_id() == graphics_thread_ && graphics_allowed_;
|
||||
}
|
||||
|
||||
void AppAdapterApple::DoPushGraphicsContextRunnable(Runnable* runnable) {
|
||||
// In strict mode, make sure we're in our TryRender() call.
|
||||
auto lock = std::scoped_lock(graphics_calls_mutex_);
|
||||
if (graphics_calls_.size() > 1000) {
|
||||
BA_LOG_ONCE(LogLevel::kError, "graphics_calls_ got too big.");
|
||||
}
|
||||
graphics_calls_.push_back(runnable);
|
||||
}
|
||||
|
||||
auto AppAdapterApple::ShouldUseCursor() -> bool {
|
||||
// On Mac of course we want our nice custom hardware cursor.
|
||||
if (g_buildconfig.ostype_macos()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Anywhere else (iOS, tvOS, etc.) just say no cursor for now. The OS
|
||||
// may draw one in some cases (trackpad connected to iPad, etc.) but we
|
||||
// don't interfere and just let the OS draw its normal cursor in that
|
||||
// case. Can revisit this later if that becomes a more common scenario.
|
||||
return false;
|
||||
}
|
||||
|
||||
auto AppAdapterApple::HasHardwareCursor() -> bool {
|
||||
// (mac should be only build getting called here)
|
||||
assert(g_buildconfig.ostype_macos());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AppAdapterApple::SetHardwareCursorVisible(bool visible) {
|
||||
// (mac should be only build getting called here)
|
||||
assert(g_buildconfig.ostype_macos());
|
||||
assert(g_core->InMainThread());
|
||||
|
||||
#if BA_OSTYPE_MACOS
|
||||
BallisticaKit::CocoaSupportSetCursorVisible(visible);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AppAdapterApple::TerminateApp() {
|
||||
#if BA_OSTYPE_MACOS
|
||||
BallisticaKit::CocoaSupportTerminateApp();
|
||||
#else
|
||||
AppAdapter::TerminateApp();
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
@ -5,18 +5,55 @@
|
||||
|
||||
#if BA_XCODE_BUILD
|
||||
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "ballistica/base/app_adapter/app_adapter.h"
|
||||
#include "ballistica/shared/generic/runnable.h"
|
||||
|
||||
namespace ballistica::base {
|
||||
|
||||
class AppAdapterApple : public AppAdapter {
|
||||
public:
|
||||
/// Given base, returns app-adapter cast to our type. This assumes it
|
||||
/// actually *is* our type.
|
||||
static auto Get(BaseFeatureSet* base) -> AppAdapterApple* {
|
||||
auto* val = static_cast<AppAdapterApple*>(base->app_adapter);
|
||||
assert(val);
|
||||
assert(dynamic_cast<AppAdapterApple*>(base->app_adapter) == val);
|
||||
return val;
|
||||
}
|
||||
|
||||
auto ManagesMainThreadEventLoop() const -> bool override;
|
||||
void DoApplyAppConfig() override;
|
||||
|
||||
/// Called by FromSwift.
|
||||
auto TryRender() -> bool;
|
||||
|
||||
/// Called by FromSwift.
|
||||
void SetScreenResolution(float pixel_width, float pixel_height);
|
||||
|
||||
protected:
|
||||
void DoPushMainThreadRunnable(Runnable* runnable) override;
|
||||
void DoPushGraphicsContextRunnable(Runnable* runnable) override;
|
||||
auto InGraphicsContext() -> bool override;
|
||||
auto ShouldUseCursor() -> bool override;
|
||||
auto HasHardwareCursor() -> bool override;
|
||||
void SetHardwareCursorVisible(bool visible) override;
|
||||
void TerminateApp() override;
|
||||
|
||||
private:
|
||||
void UpdateScreenSizes_();
|
||||
class ScopedAllowGraphics_;
|
||||
void SetScreen_(TextureQualityRequest texture_quality_requested,
|
||||
GraphicsQualityRequest graphics_quality_requested);
|
||||
void ReloadRenderer_(GraphicsQualityRequest graphics_quality_requested,
|
||||
TextureQualityRequest texture_quality_requested);
|
||||
std::thread::id graphics_thread_{};
|
||||
bool graphics_allowed_;
|
||||
std::mutex graphics_calls_mutex_;
|
||||
std::vector<Runnable*> graphics_calls_;
|
||||
};
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
// Released under the MIT License. See LICENSE for details.
|
||||
|
||||
#include "ballistica/shared/buildconfig/buildconfig_common.h"
|
||||
#if BA_SDL_BUILD
|
||||
|
||||
#include "ballistica/base/app_adapter/app_adapter_sdl.h"
|
||||
|
||||
#include "ballistica/base/base.h"
|
||||
#include "ballistica/base/dynamics/bg/bg_dynamics.h"
|
||||
#include "ballistica/base/graphics/gl/gl_sys.h"
|
||||
@ -17,10 +17,28 @@
|
||||
#include "ballistica/base/support/app_config.h"
|
||||
#include "ballistica/base/ui/ui.h"
|
||||
#include "ballistica/core/platform/core_platform.h"
|
||||
#include "ballistica/shared/buildconfig/buildconfig_common.h"
|
||||
#include "ballistica/shared/foundation/event_loop.h"
|
||||
|
||||
namespace ballistica::base {
|
||||
|
||||
/// RAII-friendly way to mark where in the main thread we're allowed to run
|
||||
/// graphics code (only applies in strict-graphics-context mode).
|
||||
class AppAdapterSDL::ScopedAllowGraphics_ {
|
||||
public:
|
||||
explicit ScopedAllowGraphics_(AppAdapterSDL* adapter) : adapter_{adapter} {
|
||||
assert(!adapter_->strict_graphics_allowed_);
|
||||
adapter->strict_graphics_allowed_ = true;
|
||||
}
|
||||
~ScopedAllowGraphics_() {
|
||||
assert(adapter_->strict_graphics_allowed_);
|
||||
adapter_->strict_graphics_allowed_ = false;
|
||||
}
|
||||
|
||||
private:
|
||||
AppAdapterSDL* adapter_;
|
||||
};
|
||||
|
||||
AppAdapterSDL::AppAdapterSDL() {
|
||||
assert(!g_core->HeadlessMode());
|
||||
assert(g_core->InMainThread());
|
||||
@ -36,6 +54,11 @@ void AppAdapterSDL::OnMainThreadStartApp() {
|
||||
// App is starting. Let's fire up the ol' SDL.
|
||||
uint32_t sdl_flags{SDL_INIT_VIDEO | SDL_INIT_JOYSTICK};
|
||||
|
||||
if (strict_graphics_context_) {
|
||||
Log(LogLevel::kWarning,
|
||||
"AppAdapterSDL strict_graphics_context_ is enabled."
|
||||
" Remember to turn this off.");
|
||||
}
|
||||
// We may or may not want xinput on windows.
|
||||
if (g_buildconfig.ostype_windows()) {
|
||||
if (!g_core->platform->GetLowLevelConfigValue("enablexinput", 1)) {
|
||||
@ -72,6 +95,9 @@ void AppAdapterSDL::OnMainThreadStartApp() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We currently use a software cursor, so hide the system one.
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
}
|
||||
|
||||
void AppAdapterSDL::DoApplyAppConfig() {
|
||||
@ -91,6 +117,7 @@ void AppAdapterSDL::DoApplyAppConfig() {
|
||||
// g_base->app_config->Resolve(AppConfig::StringID::kResolutionAndroid);
|
||||
|
||||
bool fullscreen = g_base->app_config->Resolve(AppConfig::BoolID::kFullscreen);
|
||||
fullscreen = false;
|
||||
auto vsync = g_base->graphics->VSyncFromAppConfig();
|
||||
int max_fps = g_base->app_config->Resolve(AppConfig::IntID::kMaxFPS);
|
||||
|
||||
@ -114,7 +141,7 @@ void AppAdapterSDL::RunMainThreadEventLoopToCompletion() {
|
||||
}
|
||||
|
||||
// Draw.
|
||||
if (!hidden_ && g_base->graphics_server->TryRender()) {
|
||||
if (!hidden_ && TryRender()) {
|
||||
SDL_GL_SwapWindow(sdl_window_);
|
||||
}
|
||||
|
||||
@ -125,6 +152,34 @@ void AppAdapterSDL::RunMainThreadEventLoopToCompletion() {
|
||||
}
|
||||
}
|
||||
|
||||
auto AppAdapterSDL::TryRender() -> bool {
|
||||
if (strict_graphics_context_) {
|
||||
// In strict mode, allow graphics stuff in here. Normally we allow it
|
||||
// anywhere in the main thread.
|
||||
auto allow = ScopedAllowGraphics_(this);
|
||||
|
||||
// Run & release any pending runnables.
|
||||
std::vector<Runnable*> calls;
|
||||
{
|
||||
// Pull calls off the list before running them; this way we only need
|
||||
// to grab the list lock for a moment.
|
||||
auto lock = std::scoped_lock(strict_graphics_calls_mutex_);
|
||||
if (!strict_graphics_calls_.empty()) {
|
||||
strict_graphics_calls_.swap(calls);
|
||||
}
|
||||
}
|
||||
for (auto* call : calls) {
|
||||
call->RunAndLogErrors();
|
||||
delete call;
|
||||
}
|
||||
// Lastly render.
|
||||
return g_base->graphics_server->TryRender();
|
||||
} else {
|
||||
// Simple path; just render.
|
||||
return g_base->graphics_server->TryRender();
|
||||
}
|
||||
}
|
||||
|
||||
void AppAdapterSDL::SleepUntilNextEventCycle_(microsecs_t cycle_start_time) {
|
||||
// Special case: if we're hidden, we simply sleep for a long bit; no fancy
|
||||
// timing.
|
||||
@ -300,7 +355,13 @@ void AppAdapterSDL::HandleSDLEvent_(const SDL_Event& event) {
|
||||
break;
|
||||
|
||||
case SDL_QUIT:
|
||||
g_base->logic->event_loop()->PushCall([] { g_base->ui->ConfirmQuit(); });
|
||||
if (g_core->GetAppTimeMillisecs() - last_windowevent_close_time_ < 100) {
|
||||
// If they hit the window close button, skip the confirm.
|
||||
g_base->QuitApp(false);
|
||||
} else {
|
||||
// By default, confirm before quitting.
|
||||
g_base->QuitApp(true);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_TEXTINPUT: {
|
||||
@ -310,6 +371,13 @@ void AppAdapterSDL::HandleSDLEvent_(const SDL_Event& event) {
|
||||
|
||||
case SDL_WINDOWEVENT: {
|
||||
switch (event.window.event) {
|
||||
case SDL_WINDOWEVENT_CLOSE: {
|
||||
// Simply note that this happened. We use this to adjust our
|
||||
// SDL_QUIT behavior (quit is called right after this).
|
||||
last_windowevent_close_time_ = g_core->GetAppTimeMillisecs();
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_WINDOWEVENT_MAXIMIZED: {
|
||||
if (g_buildconfig.ostype_macos() && !fullscreen_) {
|
||||
// Special case: on Mac, we wind up here if someone fullscreens
|
||||
@ -476,9 +544,12 @@ void AppAdapterSDL::SetScreen_(
|
||||
bool fullscreen, int max_fps, VSyncRequest vsync_requested,
|
||||
TextureQualityRequest texture_quality_requested,
|
||||
GraphicsQualityRequest graphics_quality_requested) {
|
||||
assert(InGraphicsContext());
|
||||
assert(g_core->InMainThread());
|
||||
assert(!g_core->HeadlessMode());
|
||||
|
||||
// In strict mode, allow graphics stuff in here.
|
||||
auto allow = ScopedAllowGraphics_(this);
|
||||
|
||||
// If we know what we support, filter our request types to what is
|
||||
// supported. This will keep us from rebuilding contexts if request type
|
||||
// is flipping between different types that we don't support.
|
||||
@ -676,6 +747,74 @@ void AppAdapterSDL::UpdateScreenSizes_() {
|
||||
static_cast<float>(pixels_y));
|
||||
}
|
||||
|
||||
/// As a default, allow graphics stuff in the main thread.
|
||||
auto AppAdapterSDL::InGraphicsContext() -> bool {
|
||||
// In strict mode, make sure we're in the right thread *and* within our
|
||||
// render call.
|
||||
if (strict_graphics_context_) {
|
||||
return g_core->InMainThread() && strict_graphics_allowed_;
|
||||
}
|
||||
// By default, allow anywhere in main thread.
|
||||
return g_core->InMainThread();
|
||||
}
|
||||
|
||||
void AppAdapterSDL::DoPushGraphicsContextRunnable(Runnable* runnable) {
|
||||
// In strict mode, make sure we're in our TryRender() call.
|
||||
if (strict_graphics_context_) {
|
||||
auto lock = std::scoped_lock(strict_graphics_calls_mutex_);
|
||||
if (strict_graphics_calls_.size() > 1000) {
|
||||
BA_LOG_ONCE(LogLevel::kError, "strict_graphics_calls_ got too big.");
|
||||
}
|
||||
strict_graphics_calls_.push_back(runnable);
|
||||
} else {
|
||||
DoPushMainThreadRunnable(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
void AppAdapterSDL::CursorPositionForDraw(float* x, float* y) {
|
||||
// Note: disabling this code, but leaving it in here for now as a proof of
|
||||
// concept in case its worth revisiting later. In my current tests on Mac,
|
||||
// Windows, and Linux, I'm seeing basicaly zero difference between
|
||||
// immediate calculated values and ones from the event system, so I'm
|
||||
// guessing remaining latency might be coming from the fact that frames
|
||||
// tend to get assembled 1/60th of a second before it is displayed or
|
||||
// whatnot. It'd probably be a better use of time to just wire up hardware
|
||||
// cursor support for this build.
|
||||
if (explicit_bool(true)) {
|
||||
AppAdapter::CursorPositionForDraw(x, y);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(x && y);
|
||||
|
||||
// Grab latest values from the input subsystem (what would get used by
|
||||
// default).
|
||||
float event_x = g_base->input->cursor_pos_x();
|
||||
float event_y = g_base->input->cursor_pos_y();
|
||||
|
||||
// Now ask sdl for it's latest values and wrangle the math ourself.
|
||||
int sdl_x, sdl_y;
|
||||
SDL_GetMouseState(&sdl_x, &sdl_y);
|
||||
|
||||
// Convert window coords to normalized.
|
||||
float normalized_x = static_cast<float>(sdl_x) / window_size_.x;
|
||||
float normalized_y = 1.0f - static_cast<float>(sdl_y) / window_size_.y;
|
||||
|
||||
// Convert normalized coords to virtual coords.
|
||||
float immediate_x = g_base->graphics->PixelToVirtualX(
|
||||
normalized_x * g_base->graphics->screen_pixel_width());
|
||||
float immediate_y = g_base->graphics->PixelToVirtualY(
|
||||
normalized_y * g_base->graphics->screen_pixel_height());
|
||||
|
||||
float diff_x = immediate_x - event_x;
|
||||
float diff_y = immediate_y - event_y;
|
||||
printf("DIFFS: %.2f %.2f\n", diff_x, diff_y);
|
||||
fflush(stdout);
|
||||
|
||||
*x = immediate_x;
|
||||
*y = immediate_y;
|
||||
}
|
||||
|
||||
auto AppAdapterSDL::CanToggleFullscreen() -> bool const { return true; }
|
||||
auto AppAdapterSDL::SupportsVSync() -> bool const { return true; }
|
||||
auto AppAdapterSDL::SupportsMaxFPS() -> bool const { return true; }
|
||||
|
||||
@ -32,6 +32,8 @@ class AppAdapterSDL : public AppAdapter {
|
||||
void OnMainThreadStartApp() override;
|
||||
void DoApplyAppConfig() override;
|
||||
|
||||
auto TryRender() -> bool;
|
||||
|
||||
auto CanToggleFullscreen() -> bool const override;
|
||||
auto SupportsVSync() -> bool const override;
|
||||
auto SupportsMaxFPS() -> bool const override;
|
||||
@ -40,8 +42,12 @@ class AppAdapterSDL : public AppAdapter {
|
||||
void DoPushMainThreadRunnable(Runnable* runnable) override;
|
||||
void RunMainThreadEventLoopToCompletion() override;
|
||||
void DoExitMainThreadEventLoop() override;
|
||||
auto InGraphicsContext() -> bool override;
|
||||
void DoPushGraphicsContextRunnable(Runnable* runnable) override;
|
||||
void CursorPositionForDraw(float* x, float* y) override;
|
||||
|
||||
private:
|
||||
class ScopedAllowGraphics_;
|
||||
void SetScreen_(bool fullscreen, int max_fps, VSyncRequest vsync_requested,
|
||||
TextureQualityRequest texture_quality_requested,
|
||||
GraphicsQualityRequest graphics_quality_requested);
|
||||
@ -60,11 +66,23 @@ class AppAdapterSDL : public AppAdapter {
|
||||
void RemoveSDLInputDevice_(int index);
|
||||
void SleepUntilNextEventCycle_(microsecs_t cycle_start_time);
|
||||
|
||||
bool done_{};
|
||||
bool fullscreen_{};
|
||||
bool vsync_actually_enabled_{};
|
||||
bool debug_log_sdl_frame_timing_{};
|
||||
bool hidden_{};
|
||||
bool done_ : 1 {};
|
||||
bool fullscreen_ : 1 {};
|
||||
bool vsync_actually_enabled_ : 1 {};
|
||||
bool debug_log_sdl_frame_timing_ : 1 {};
|
||||
bool hidden_ : 1 {};
|
||||
|
||||
/// With this off, graphics call pushes simply get pushed to the main
|
||||
/// thread and graphics code is allowed to run any time in the main
|
||||
/// thread. When this is on, pushed graphics-context calls get enqueued
|
||||
/// and run as part of drawing, and graphics context calls are only
|
||||
/// allowed during draws. This strictness is generally not needed here but
|
||||
/// can be useful to test with, as it more closely matches other platforms
|
||||
/// that require such a setup.
|
||||
bool strict_graphics_context_ : 1 {};
|
||||
bool strict_graphics_allowed_ : 1 {};
|
||||
std::mutex strict_graphics_calls_mutex_;
|
||||
std::vector<Runnable*> strict_graphics_calls_;
|
||||
VSync vsync_{VSync::kUnset};
|
||||
uint32_t sdl_runnable_event_id_{};
|
||||
int max_fps_{60};
|
||||
@ -73,6 +91,7 @@ class AppAdapterSDL : public AppAdapter {
|
||||
Vector2f window_size_{1.0f, 1.0f};
|
||||
SDL_Window* sdl_window_{};
|
||||
void* sdl_gl_context_{};
|
||||
millisecs_t last_windowevent_close_time_{};
|
||||
};
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
@ -1327,6 +1327,10 @@ void Assets::SetLanguageKeys(
|
||||
std::scoped_lock lock(language_mutex_);
|
||||
language_ = language;
|
||||
}
|
||||
// Log our unique change state so things that go inactive and stop
|
||||
// receiving callbacks can see if they're out of date if they become
|
||||
// active again.
|
||||
language_state_++;
|
||||
|
||||
// Let some subsystems know that language has changed.
|
||||
g_base->app_mode()->LanguageChanged();
|
||||
|
||||
@ -115,6 +115,8 @@ class Assets {
|
||||
|
||||
auto sys_assets_loaded() const { return sys_assets_loaded_; }
|
||||
|
||||
auto language_state() const { return language_state_; }
|
||||
|
||||
private:
|
||||
static void MarkAssetForLoad(Asset* c);
|
||||
void LoadSystemTexture(SysTextureID id, const char* name);
|
||||
@ -175,6 +177,7 @@ class Assets {
|
||||
// Text & Language (need to mold this into more asset-like concepts).
|
||||
std::mutex language_mutex_;
|
||||
std::unordered_map<std::string, std::string> language_;
|
||||
int language_state_{};
|
||||
std::mutex special_char_mutex_;
|
||||
std::unordered_map<SpecialChar, std::string> special_char_strings_;
|
||||
};
|
||||
|
||||
@ -15,7 +15,7 @@ AssetsServer::AssetsServer() = default;
|
||||
void AssetsServer::OnMainThreadStartApp() {
|
||||
// Spin up our thread.
|
||||
event_loop_ = new EventLoop(EventLoopID::kAssets);
|
||||
g_core->pausable_event_loops.push_back(event_loop_);
|
||||
g_core->suspendable_event_loops.push_back(event_loop_);
|
||||
|
||||
event_loop_->PushCallSynchronous([this] { OnAppStartInThread(); });
|
||||
}
|
||||
|
||||
@ -11,8 +11,8 @@ namespace ballistica::base {
|
||||
// this is provided by the renderer
|
||||
class MeshAssetRendererData : public Object {
|
||||
public:
|
||||
auto GetDefaultOwnerThread() const -> EventLoopID override {
|
||||
return EventLoopID::kMain;
|
||||
auto GetThreadOwnership() const -> ThreadOwnership override {
|
||||
return ThreadOwnership::kGraphicsContext;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -5,14 +5,12 @@
|
||||
#include "ballistica/base/app_adapter/app_adapter.h"
|
||||
#include "ballistica/base/assets/texture_asset_preload_data.h"
|
||||
#include "ballistica/base/assets/texture_asset_renderer_data.h"
|
||||
#include "ballistica/base/graphics/graphics.h"
|
||||
#include "ballistica/base/graphics/graphics_server.h"
|
||||
#include "ballistica/base/graphics/renderer/renderer.h"
|
||||
#include "ballistica/base/graphics/text/text_packer.h"
|
||||
#include "ballistica/base/graphics/texture/dds.h"
|
||||
#include "ballistica/base/graphics/texture/ktx.h"
|
||||
#include "ballistica/base/graphics/texture/pvr.h"
|
||||
#include "ballistica/core/core.h"
|
||||
#include "external/qr_code_generator/QrCode.hpp"
|
||||
|
||||
namespace ballistica::base {
|
||||
|
||||
@ -10,8 +10,8 @@ namespace ballistica::base {
|
||||
// Renderer-specific data (gl tex, etc). To be extended by the renderer.
|
||||
class TextureAssetRendererData : public Object {
|
||||
public:
|
||||
auto GetDefaultOwnerThread() const -> EventLoopID override {
|
||||
return EventLoopID::kMain;
|
||||
auto GetThreadOwnership() const -> ThreadOwnership override {
|
||||
return ThreadOwnership::kGraphicsContext;
|
||||
}
|
||||
|
||||
// Create the renderer data but don't load it in yet.
|
||||
|
||||
@ -136,14 +136,14 @@ AudioServer::AudioServer() : impl_{new AudioServer::Impl()} {}
|
||||
void AudioServer::OnMainThreadStartApp() {
|
||||
// Spin up our thread.
|
||||
event_loop_ = new EventLoop(EventLoopID::kAudio);
|
||||
g_core->pausable_event_loops.push_back(event_loop_);
|
||||
g_core->suspendable_event_loops.push_back(event_loop_);
|
||||
|
||||
// Run some setup stuff from our shiny new thread.
|
||||
event_loop_->PushCall([this] {
|
||||
// We want to be informed when our event-loop is pausing and unpausing.
|
||||
event_loop()->AddPauseCallback(
|
||||
event_loop()->AddSuspendCallback(
|
||||
NewLambdaRunnableUnmanaged([this] { OnThreadPause(); }));
|
||||
event_loop()->AddResumeCallback(
|
||||
event_loop()->AddUnsuspendCallback(
|
||||
NewLambdaRunnableUnmanaged([this] { OnThreadResume(); }));
|
||||
});
|
||||
|
||||
@ -1136,7 +1136,7 @@ void AudioServer::PushSetSoundPitchCall(float val) {
|
||||
event_loop()->PushCall([this, val] { SetSoundPitch(val); });
|
||||
}
|
||||
|
||||
void AudioServer::PushSetPausedCall(bool pause) {
|
||||
void AudioServer::PushSetSuspendedCall(bool pause) {
|
||||
event_loop()->PushCall([this, pause] {
|
||||
if (g_buildconfig.ostype_android()) {
|
||||
Log(LogLevel::kError, "Shouldn't be getting SetPausedCall on android.");
|
||||
@ -1189,7 +1189,7 @@ void AudioServer::ClearSoundRefDeleteList() {
|
||||
|
||||
void AudioServer::BeginInterruption() {
|
||||
assert(!g_base->InAudioThread());
|
||||
g_base->audio_server->PushSetPausedCall(true);
|
||||
g_base->audio_server->PushSetSuspendedCall(true);
|
||||
|
||||
// Wait a reasonable amount of time for the thread to act on it.
|
||||
millisecs_t t = g_core->GetAppTimeMillisecs();
|
||||
@ -1211,7 +1211,7 @@ void AudioServer::OnThreadResume() { SetPaused(false); }
|
||||
|
||||
void AudioServer::EndInterruption() {
|
||||
assert(!g_base->InAudioThread());
|
||||
g_base->audio_server->PushSetPausedCall(false);
|
||||
g_base->audio_server->PushSetSuspendedCall(false);
|
||||
|
||||
// Wait a reasonable amount of time for the thread to act on it.
|
||||
millisecs_t t = g_core->GetAppTimeMillisecs();
|
||||
|
||||
@ -28,7 +28,7 @@ class AudioServer {
|
||||
|
||||
void PushSetVolumesCall(float music_volume, float sound_volume);
|
||||
void PushSetSoundPitchCall(float val);
|
||||
void PushSetPausedCall(bool pause);
|
||||
void PushSetSuspendedCall(bool pause);
|
||||
|
||||
static void BeginInterruption();
|
||||
static void EndInterruption();
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
#include "ballistica/base/support/stress_test.h"
|
||||
#include "ballistica/base/ui/dev_console.h"
|
||||
#include "ballistica/base/ui/ui.h"
|
||||
#include "ballistica/base/ui/ui_delegate.h"
|
||||
#include "ballistica/core/python/core_python.h"
|
||||
#include "ballistica/shared/foundation/event_loop.h"
|
||||
#include "ballistica/shared/foundation/logging.h"
|
||||
@ -226,7 +227,7 @@ void BaseFeatureSet::OnAppShutdownComplete() {
|
||||
if (app_adapter->ManagesMainThreadEventLoop()) {
|
||||
app_adapter->DoExitMainThreadEventLoop();
|
||||
} else {
|
||||
platform->TerminateApp();
|
||||
app_adapter->TerminateApp();
|
||||
}
|
||||
}
|
||||
|
||||
@ -297,10 +298,6 @@ void BaseFeatureSet::RunAppToCompletion() {
|
||||
g_base->app_adapter->RunMainThreadEventLoopToCompletion();
|
||||
}
|
||||
|
||||
// void BaseFeatureSet::PrimeAppMainThreadEventPump() {
|
||||
// app_adapter->PrimeMainThreadEventPump();
|
||||
// }
|
||||
|
||||
auto BaseFeatureSet::HavePlus() -> bool {
|
||||
if (!plus_soft_ && !tried_importing_plus_) {
|
||||
python->SoftImportPlus();
|
||||
@ -363,37 +360,6 @@ void BaseFeatureSet::set_classic(ClassicSoftInterface* classic) {
|
||||
classic_soft_ = classic;
|
||||
}
|
||||
|
||||
auto BaseFeatureSet::HaveUIV1() -> bool {
|
||||
if (!ui_v1_soft_ && !tried_importing_ui_v1_) {
|
||||
python->SoftImportUIV1();
|
||||
// Important to set this *after* import attempt, or a second import
|
||||
// attempt while first is ongoing can insta-fail. Multiple import
|
||||
// attempts shouldn't hurt anything.
|
||||
tried_importing_ui_v1_ = true;
|
||||
}
|
||||
return ui_v1_soft_ != nullptr;
|
||||
}
|
||||
|
||||
/// Access the plus feature-set. Will throw an exception if not present.
|
||||
auto BaseFeatureSet::ui_v1() -> UIV1SoftInterface* {
|
||||
if (!ui_v1_soft_ && !tried_importing_ui_v1_) {
|
||||
python->SoftImportUIV1();
|
||||
// Important to set this *after* import attempt, or a second import
|
||||
// attempt while first is ongoing can insta-fail. Multiple import
|
||||
// attempts shouldn't hurt anything.
|
||||
tried_importing_ui_v1_ = true;
|
||||
}
|
||||
if (!ui_v1_soft_) {
|
||||
throw Exception("ui_v1 feature-set not present.");
|
||||
}
|
||||
return ui_v1_soft_;
|
||||
}
|
||||
|
||||
void BaseFeatureSet::set_ui_v1(UIV1SoftInterface* ui_v1) {
|
||||
assert(ui_v1_soft_ == nullptr);
|
||||
ui_v1_soft_ = ui_v1;
|
||||
}
|
||||
|
||||
auto BaseFeatureSet::GetAppInstanceUUID() -> const std::string& {
|
||||
static std::string app_instance_uuid;
|
||||
static bool have_app_instance_uuid = false;
|
||||
@ -489,6 +455,10 @@ auto BaseFeatureSet::InNetworkWriteThread() const -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto BaseFeatureSet::InGraphicsContext() const -> bool {
|
||||
return app_adapter->InGraphicsContext();
|
||||
}
|
||||
|
||||
void BaseFeatureSet::ScreenMessage(const std::string& s,
|
||||
const Vector3f& color) {
|
||||
logic->event_loop()->PushCall(
|
||||
@ -719,15 +689,25 @@ void BaseFeatureSet::ShutdownSuppressDisallow() {
|
||||
|
||||
auto BaseFeatureSet::GetReturnValue() const -> int { return return_value(); }
|
||||
|
||||
void BaseFeatureSet::QuitApp(QuitType quit_type) {
|
||||
// If they ask for 'back' and we support that, do it.
|
||||
// Otherwise if they want 'back' or 'soft' and we support soft, do it.
|
||||
// Otherwise go with a regular app shutdown.
|
||||
if (quit_type == QuitType::kBack && g_base->platform->CanBackQuit()) {
|
||||
logic->event_loop()->PushCall([this] { platform->DoBackQuit(); });
|
||||
void BaseFeatureSet::QuitApp(bool confirm, QuitType quit_type) {
|
||||
// If they want a confirm dialog and we're able to present one, do that.
|
||||
if (confirm && !g_core->HeadlessMode() && !g_base->input->IsInputLocked()
|
||||
&& g_base->ui->delegate()
|
||||
&& g_base->ui->delegate()->HasQuitConfirmDialog()) {
|
||||
logic->event_loop()->PushCall(
|
||||
[this, quit_type] { g_base->ui->delegate()->ConfirmQuit(quit_type); });
|
||||
return;
|
||||
}
|
||||
// Ok looks like we're quitting immediately.
|
||||
//
|
||||
// If they ask for 'back' and we support that, do it. Otherwise if they
|
||||
// want 'back' or 'soft' and we support soft, do it. Otherwise go with a
|
||||
// regular app shutdown.
|
||||
if (quit_type == QuitType::kBack && app_adapter->CanBackQuit()) {
|
||||
logic->event_loop()->PushCall([this] { app_adapter->DoBackQuit(); });
|
||||
} else if ((quit_type == QuitType::kBack || quit_type == QuitType::kSoft)
|
||||
&& g_base->platform->CanSoftQuit()) {
|
||||
logic->event_loop()->PushCall([this] { platform->DoSoftQuit(); });
|
||||
&& app_adapter->CanSoftQuit()) {
|
||||
logic->event_loop()->PushCall([this] { app_adapter->DoSoftQuit(); });
|
||||
} else {
|
||||
logic->event_loop()->PushCall([this] { logic->Shutdown(); });
|
||||
}
|
||||
|
||||
@ -115,22 +115,10 @@ class TextureAssetPreloadData;
|
||||
class TextureAssetRendererData;
|
||||
class TouchInput;
|
||||
class UI;
|
||||
class UIV1SoftInterface;
|
||||
class UIDelegateInterface;
|
||||
class AppAdapterVR;
|
||||
class GraphicsVR;
|
||||
|
||||
enum class QuitType : uint8_t {
|
||||
/// Leads to the process exiting.
|
||||
kHard,
|
||||
/// May hide/reset the app but keep the process running. Generally how
|
||||
/// mobile apps behave.
|
||||
kSoft,
|
||||
/// Same as kSoft but gives 'back-button-pressed' behavior which may
|
||||
/// differ depending on the OS (returning to a previous Activity in
|
||||
/// Android instead of dumping to the home screen, etc.)
|
||||
kBack,
|
||||
};
|
||||
|
||||
enum class AssetType : uint8_t {
|
||||
kTexture,
|
||||
kCollisionMesh,
|
||||
@ -619,9 +607,11 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
|
||||
/// 'soft' means the app can simply reset/hide itself instead of actually
|
||||
/// exiting the process (common behavior on mobile platforms). 'back'
|
||||
/// means that a soft-quit should behave as if a back-button was pressed,
|
||||
/// whic may trigger different behavior in the OS than a standard soft
|
||||
/// quit.
|
||||
void QuitApp(QuitType quit_type = QuitType::kSoft);
|
||||
/// which may trigger different behavior in the OS than a standard soft
|
||||
/// quit. If 'confirm' is true, a confirmation dialog will be presented if
|
||||
/// the current app-mode provides one and the app is in gui mode.
|
||||
/// Otherwise the quit will be immediate.
|
||||
void QuitApp(bool confirm = false, QuitType quit_type = QuitType::kSoft);
|
||||
|
||||
/// Called when app shutdown process completes. Sets app to exit.
|
||||
void OnAppShutdownComplete();
|
||||
@ -657,14 +647,6 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
|
||||
|
||||
void set_classic(ClassicSoftInterface* classic);
|
||||
|
||||
/// Try to load the ui_v1 feature-set and return whether it is available.
|
||||
auto HaveUIV1() -> bool;
|
||||
|
||||
/// Access the ui_v1 feature-set. Will throw an exception if not present.
|
||||
auto ui_v1() -> UIV1SoftInterface*;
|
||||
|
||||
void set_ui_v1(UIV1SoftInterface* ui_v1);
|
||||
|
||||
/// Return a string that should be universally unique to this particular
|
||||
/// running instance of the app.
|
||||
auto GetAppInstanceUUID() -> const std::string&;
|
||||
@ -686,6 +668,7 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
|
||||
auto InAudioThread() const -> bool override;
|
||||
auto InBGDynamicsThread() const -> bool override;
|
||||
auto InNetworkWriteThread() const -> bool override;
|
||||
auto InGraphicsContext() const -> bool override;
|
||||
|
||||
/// High level screen-message call usable from any thread.
|
||||
void ScreenMessage(const std::string& s, const Vector3f& color) override;
|
||||
@ -755,9 +738,10 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
|
||||
Utils* const utils;
|
||||
|
||||
// Variable subsystems.
|
||||
auto* app_mode() const { return app_mode_; }
|
||||
auto* stress_test() const { return stress_test_; }
|
||||
void set_app_mode(AppMode* mode);
|
||||
auto* app_mode() const { return app_mode_; }
|
||||
|
||||
auto* stress_test() const { return stress_test_; }
|
||||
|
||||
/// Whether we're running under ballisticakit_server.py
|
||||
/// (affects some app behavior).
|
||||
@ -781,7 +765,6 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
|
||||
AppMode* app_mode_;
|
||||
PlusSoftInterface* plus_soft_{};
|
||||
ClassicSoftInterface* classic_soft_{};
|
||||
UIV1SoftInterface* ui_v1_soft_{};
|
||||
StressTest* stress_test_;
|
||||
|
||||
std::mutex shutdown_suppress_lock_;
|
||||
|
||||
@ -142,7 +142,7 @@ void BGDynamics::SetDrawSnapshot(BGDynamicsDrawSnapshot* s) {
|
||||
}
|
||||
|
||||
void BGDynamics::TooSlow() {
|
||||
if (!EventLoop::AreEventLoopsPaused()) {
|
||||
if (!EventLoop::AreEventLoopsSuspended()) {
|
||||
g_base->bg_dynamics_server->PushTooSlowCall();
|
||||
}
|
||||
}
|
||||
|
||||
@ -691,7 +691,7 @@ BGDynamicsServer::BGDynamicsServer()
|
||||
void BGDynamicsServer::OnMainThreadStartApp() {
|
||||
// Spin up our thread.
|
||||
event_loop_ = new EventLoop(EventLoopID::kBGDynamics);
|
||||
g_core->pausable_event_loops.push_back(event_loop_);
|
||||
g_core->suspendable_event_loops.push_back(event_loop_);
|
||||
}
|
||||
|
||||
BGDynamicsServer::Tendril::~Tendril() {
|
||||
|
||||
@ -14,8 +14,8 @@ namespace ballistica::base {
|
||||
// Base class for fragment/vertex shaders.
|
||||
class RendererGL::ShaderGL : public Object {
|
||||
public:
|
||||
auto GetDefaultOwnerThread() const -> EventLoopID override {
|
||||
return EventLoopID::kMain;
|
||||
auto GetThreadOwnership() const -> ThreadOwnership override {
|
||||
return ThreadOwnership::kGraphicsContext;
|
||||
}
|
||||
|
||||
ShaderGL(GLenum type_in, const std::string& src_in) : type_(type_in) {
|
||||
|
||||
@ -2080,7 +2080,7 @@ void RendererGL::ProcessRenderCommandBuffer(RenderCommandBuffer* buffer,
|
||||
}
|
||||
case RenderCommandBuffer::Command::kCursorTranslate: {
|
||||
float x, y;
|
||||
g_base->platform->GetCursorPosition(&x, &y);
|
||||
g_base->app_adapter->CursorPositionForDraw(&x, &y);
|
||||
g_base->graphics_server->Translate(Vector3f(x, y, 0));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1452,11 +1452,11 @@ void Graphics::DrawCursor(FrameDef* frame_def) {
|
||||
|
||||
millisecs_t app_time_millisecs = frame_def->app_time_millisecs();
|
||||
|
||||
bool can_show_cursor = g_core->platform->IsRunningOnDesktop();
|
||||
bool can_show_cursor = g_base->app_adapter->ShouldUseCursor();
|
||||
bool should_show_cursor =
|
||||
camera_->manual() || g_base->input->IsCursorVisible();
|
||||
|
||||
if (g_buildconfig.hardware_cursor()) {
|
||||
if (g_base->app_adapter->HasHardwareCursor()) {
|
||||
// If we're using a hardware cursor, ship hardware cursor visibility
|
||||
// updates to the app thread periodically.
|
||||
bool new_cursor_visibility = false;
|
||||
@ -1472,7 +1472,7 @@ void Graphics::DrawCursor(FrameDef* frame_def) {
|
||||
last_cursor_visibility_event_time_ = app_time_millisecs;
|
||||
g_base->app_adapter->PushMainThreadCall([this] {
|
||||
assert(g_core && g_core->InMainThread());
|
||||
g_base->platform->SetHardwareCursorVisible(hardware_cursor_visible_);
|
||||
g_base->app_adapter->SetHardwareCursorVisible(hardware_cursor_visible_);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@ -1486,10 +1486,10 @@ void Graphics::DrawCursor(FrameDef* frame_def) {
|
||||
auto xf = c.ScopedTransform();
|
||||
|
||||
// Note: we don't plug in known cursor position values here; we tell the
|
||||
// renderer to insert the latest values on its end; this lessens cursor
|
||||
// lag substantially.
|
||||
// renderer to insert the latest values on its end; this can lessen
|
||||
// cursor lag substantially.
|
||||
c.CursorTranslate();
|
||||
c.Translate(csize * 0.44f, csize * -0.44f, kCursorZDepth);
|
||||
c.Translate(csize * 0.40f, csize * -0.38f, kCursorZDepth);
|
||||
c.Scale(csize, csize);
|
||||
c.DrawMeshAsset(g_base->assets->SysMesh(SysMeshID::kImage1x1));
|
||||
}
|
||||
|
||||
@ -2,22 +2,9 @@
|
||||
|
||||
#include "ballistica/base/graphics/graphics_server.h"
|
||||
|
||||
// Kill this.
|
||||
#include "ballistica/base/graphics/gl/renderer_gl.h"
|
||||
|
||||
// FIXME: clear out this conditional stuff.
|
||||
#if BA_SDL_BUILD
|
||||
#include "ballistica/base/app_adapter/app_adapter_sdl.h"
|
||||
#else
|
||||
#include "ballistica/base/app_adapter/app_adapter.h"
|
||||
#endif
|
||||
|
||||
#include "ballistica/base/assets/assets.h"
|
||||
#include "ballistica/base/graphics/mesh/mesh_data.h"
|
||||
#include "ballistica/base/graphics/renderer/renderer.h"
|
||||
#include "ballistica/base/graphics/support/frame_def.h"
|
||||
#include "ballistica/base/logic/logic.h"
|
||||
#include "ballistica/core/platform/core_platform.h"
|
||||
#include "ballistica/shared/foundation/event_loop.h"
|
||||
|
||||
namespace ballistica::base {
|
||||
@ -83,7 +70,7 @@ auto GraphicsServer::WaitForRenderFrameDef_() -> FrameDef* {
|
||||
}
|
||||
|
||||
// If the app is paused, never render.
|
||||
if (g_base->app_adapter->app_paused()) {
|
||||
if (g_base->app_adapter->app_suspended()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@ -264,16 +264,6 @@ class GraphicsServer {
|
||||
|
||||
auto renderer_context_lost() const { return renderer_context_lost_; }
|
||||
|
||||
// auto fullscreen_enabled() const { return fullscreen_enabled_; }
|
||||
|
||||
// This doesn't actually toggle fullscreen. It is used to inform the game
|
||||
// when fullscreen changes under it.
|
||||
// auto set_fullscreen_enabled(bool fs) { fullscreen_enabled_ = fs; }
|
||||
|
||||
// #if BA_ENABLE_OPENGL
|
||||
// auto gl_context() const -> GLContext* { return gl_context_.get(); }
|
||||
// #endif
|
||||
|
||||
auto graphics_quality_requested() const {
|
||||
return graphics_quality_requested_;
|
||||
}
|
||||
@ -290,17 +280,8 @@ class GraphicsServer {
|
||||
|
||||
auto texture_quality_requested() const { return texture_quality_requested_; }
|
||||
|
||||
// auto initial_screen_created() const { return initial_screen_created_; }
|
||||
|
||||
void HandlePushAndroidRes(const std::string& android_res);
|
||||
|
||||
// void HandleFullContextScreenRebuild(
|
||||
// bool need_full_context_rebuild, bool fullscreen,
|
||||
// GraphicsQualityRequest graphics_quality_requested,
|
||||
// TextureQualityRequest texture_quality_requested);
|
||||
// void HandleFullscreenToggling(bool do_set_existing_fs, bool do_toggle_fs,
|
||||
// bool fullscreen);
|
||||
|
||||
private:
|
||||
// So we don't have to include app_adapter.h here for asserts.
|
||||
auto InGraphicsContext_() const -> bool;
|
||||
@ -332,53 +313,49 @@ class GraphicsServer {
|
||||
}
|
||||
}
|
||||
|
||||
float res_x_{};
|
||||
float res_y_{};
|
||||
float res_x_virtual_{};
|
||||
float res_y_virtual_{};
|
||||
uint32_t texture_compression_types_{};
|
||||
bool renderer_loaded_ : 1 {};
|
||||
bool v_sync_ : 1 {};
|
||||
bool auto_vsync_ : 1 {};
|
||||
bool model_view_projection_matrix_dirty_ : 1 {true};
|
||||
bool model_world_matrix_dirty_ : 1 {true};
|
||||
bool graphics_quality_set_ : 1 {};
|
||||
bool texture_quality_set_ : 1 {};
|
||||
bool tv_border_ : 1 {};
|
||||
bool renderer_context_lost_ : 1 {};
|
||||
bool texture_compression_types_set_ : 1 {};
|
||||
bool cam_orient_matrix_dirty_ : 1 {true};
|
||||
TextureQualityRequest texture_quality_requested_{
|
||||
TextureQualityRequest::kUnset};
|
||||
TextureQuality texture_quality_{TextureQuality::kLow};
|
||||
GraphicsQualityRequest graphics_quality_requested_{
|
||||
GraphicsQualityRequest::kUnset};
|
||||
GraphicsQuality graphics_quality_{GraphicsQuality::kUnset};
|
||||
// bool fullscreen_enabled_{};
|
||||
// float target_res_x_{800.0f};
|
||||
// float target_res_y_{600.0f};
|
||||
int render_hold_{};
|
||||
float res_x_{};
|
||||
float res_y_{};
|
||||
float res_x_virtual_{};
|
||||
float res_y_virtual_{};
|
||||
Matrix44f model_view_matrix_{kMatrix44fIdentity};
|
||||
Matrix44f view_world_matrix_{kMatrix44fIdentity};
|
||||
Matrix44f projection_matrix_{kMatrix44fIdentity};
|
||||
Matrix44f model_view_projection_matrix_{kMatrix44fIdentity};
|
||||
Matrix44f model_world_matrix_{kMatrix44fIdentity};
|
||||
std::vector<Matrix44f> model_view_stack_;
|
||||
uint32_t texture_compression_types_{};
|
||||
uint32_t projection_matrix_state_{1};
|
||||
uint32_t model_view_projection_matrix_state_{1};
|
||||
uint32_t model_world_matrix_state_{1};
|
||||
Matrix44f light_shadow_projection_matrix_{kMatrix44fIdentity};
|
||||
uint32_t light_shadow_projection_matrix_state_{1};
|
||||
uint32_t cam_pos_state_{1};
|
||||
uint32_t cam_orient_matrix_state_{1};
|
||||
Vector3f cam_pos_{0.0f, 0.0f, 0.0f};
|
||||
Vector3f cam_target_{0.0f, 0.0f, 0.0f};
|
||||
uint32_t cam_pos_state_{1};
|
||||
Matrix44f light_shadow_projection_matrix_{kMatrix44fIdentity};
|
||||
Matrix44f cam_orient_matrix_ = kMatrix44fIdentity;
|
||||
uint32_t cam_orient_matrix_state_{1};
|
||||
std::list<MeshData*> mesh_datas_;
|
||||
Timer* render_timer_{};
|
||||
Renderer* renderer_{};
|
||||
FrameDef* frame_def_{};
|
||||
// bool initial_screen_created_{};
|
||||
bool renderer_loaded_{};
|
||||
bool v_sync_{};
|
||||
bool auto_vsync_{};
|
||||
bool model_view_projection_matrix_dirty_{true};
|
||||
bool model_world_matrix_dirty_{true};
|
||||
bool graphics_quality_set_{};
|
||||
bool texture_quality_set_{};
|
||||
bool tv_border_{};
|
||||
bool renderer_context_lost_{};
|
||||
bool texture_compression_types_set_{};
|
||||
bool cam_orient_matrix_dirty_{true};
|
||||
int render_hold_{};
|
||||
std::mutex frame_def_mutex_{};
|
||||
};
|
||||
|
||||
|
||||
@ -2,8 +2,6 @@
|
||||
|
||||
#include "ballistica/base/graphics/mesh/nine_patch_mesh.h"
|
||||
|
||||
#include "ballistica/shared/foundation/macros.h"
|
||||
|
||||
namespace ballistica::base {
|
||||
|
||||
NinePatchMesh::NinePatchMesh(float x, float y, float z, float width,
|
||||
|
||||
@ -9,8 +9,8 @@ namespace ballistica::base {
|
||||
|
||||
class Framebuffer : public Object {
|
||||
public:
|
||||
auto GetDefaultOwnerThread() const -> EventLoopID override {
|
||||
return EventLoopID::kMain;
|
||||
auto GetThreadOwnership() const -> ThreadOwnership override {
|
||||
return ThreadOwnership::kGraphicsContext;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -11,8 +11,8 @@ namespace ballistica::base {
|
||||
// Encapsulates framebuffers, main windows, etc.
|
||||
class RenderTarget : public Object {
|
||||
public:
|
||||
auto GetDefaultOwnerThread() const -> EventLoopID override {
|
||||
return EventLoopID::kMain;
|
||||
auto GetThreadOwnership() const -> ThreadOwnership override {
|
||||
return ThreadOwnership::kGraphicsContext;
|
||||
}
|
||||
enum class Type { kScreen, kFramebuffer };
|
||||
explicit RenderTarget(Type type);
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
#include "ballistica/base/graphics/texture/dds.h"
|
||||
|
||||
#include "ballistica/core/core.h"
|
||||
#include "ballistica/core/platform/core_platform.h"
|
||||
|
||||
/* DDS loader written by Jon Watte 2002 */
|
||||
|
||||
@ -113,9 +113,6 @@ auto KeyboardInput::HandleKey(const SDL_Keysym* keysym, bool repeat, bool down)
|
||||
pass = true;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma ide diagnostic ignored "ConstantConditionsOC"
|
||||
|
||||
// if we're keyboard 1 we always send at least a key press event
|
||||
// along..
|
||||
if (!parent_keyboard_input_ && !pass) {
|
||||
@ -123,8 +120,6 @@ auto KeyboardInput::HandleKey(const SDL_Keysym* keysym, bool repeat, bool down)
|
||||
pass = true;
|
||||
}
|
||||
break;
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
}
|
||||
if (pass) {
|
||||
|
||||
@ -15,7 +15,6 @@
|
||||
#include "ballistica/base/support/app_config.h"
|
||||
#include "ballistica/base/ui/dev_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"
|
||||
|
||||
@ -26,10 +25,10 @@ Input::Input() = default;
|
||||
void Input::PushCreateKeyboardInputDevices() {
|
||||
assert(g_base->logic->event_loop());
|
||||
g_base->logic->event_loop()->PushCall(
|
||||
[this] { CreateKeyboardInputDevices(); });
|
||||
[this] { CreateKeyboardInputDevices_(); });
|
||||
}
|
||||
|
||||
void Input::CreateKeyboardInputDevices() {
|
||||
void Input::CreateKeyboardInputDevices_() {
|
||||
assert(g_base->InLogicThread());
|
||||
if (keyboard_input_ != nullptr || keyboard_input_2_ != nullptr) {
|
||||
Log(LogLevel::kError,
|
||||
@ -45,10 +44,10 @@ void Input::CreateKeyboardInputDevices() {
|
||||
void Input::PushDestroyKeyboardInputDevices() {
|
||||
assert(g_base->logic->event_loop());
|
||||
g_base->logic->event_loop()->PushCall(
|
||||
[this] { DestroyKeyboardInputDevices(); });
|
||||
[this] { DestroyKeyboardInputDevices_(); });
|
||||
}
|
||||
|
||||
void Input::DestroyKeyboardInputDevices() {
|
||||
void Input::DestroyKeyboardInputDevices_() {
|
||||
assert(g_base->InLogicThread());
|
||||
if (keyboard_input_ == nullptr || keyboard_input_2_ == nullptr) {
|
||||
Log(LogLevel::kError,
|
||||
@ -80,8 +79,8 @@ auto Input::GetInputDevice(const std::string& name,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto Input::GetNewNumberedIdentifier(const std::string& name,
|
||||
const std::string& identifier) -> int {
|
||||
auto Input::GetNewNumberedIdentifier_(const std::string& name,
|
||||
const std::string& identifier) -> int {
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
// Stuff like reserved_identifiers["JoyStickType"]["0x812312314"] = 2;
|
||||
@ -154,7 +153,7 @@ void Input::CreateTouchInput() {
|
||||
PushAddInputDeviceCall(touch_input_, false);
|
||||
}
|
||||
|
||||
void Input::AnnounceConnects() {
|
||||
void Input::AnnounceConnects_() {
|
||||
static bool first_print = true;
|
||||
|
||||
// For the first announcement just say "X controllers detected" and don't
|
||||
@ -205,7 +204,7 @@ void Input::AnnounceConnects() {
|
||||
newly_connected_controllers_.clear();
|
||||
}
|
||||
|
||||
void Input::AnnounceDisconnects() {
|
||||
void Input::AnnounceDisconnects_() {
|
||||
// If there's been several connected, just give a number.
|
||||
if (newly_disconnected_controllers_.size() > 1) {
|
||||
std::string s =
|
||||
@ -228,7 +227,7 @@ void Input::AnnounceDisconnects() {
|
||||
newly_disconnected_controllers_.clear();
|
||||
}
|
||||
|
||||
void Input::ShowStandardInputDeviceConnectedMessage(InputDevice* j) {
|
||||
void Input::ShowStandardInputDeviceConnectedMessage_(InputDevice* j) {
|
||||
assert(g_base->InLogicThread());
|
||||
std::string suffix;
|
||||
suffix += j->GetPersistentIdentifier();
|
||||
@ -243,10 +242,10 @@ void Input::ShowStandardInputDeviceConnectedMessage(InputDevice* j) {
|
||||
g_base->logic->DeleteAppTimer(connect_print_timer_id_);
|
||||
}
|
||||
connect_print_timer_id_ = g_base->logic->NewAppTimer(
|
||||
250, false, NewLambdaRunnable([this] { AnnounceConnects(); }));
|
||||
250, false, NewLambdaRunnable([this] { AnnounceConnects_(); }));
|
||||
}
|
||||
|
||||
void Input::ShowStandardInputDeviceDisconnectedMessage(InputDevice* j) {
|
||||
void Input::ShowStandardInputDeviceDisconnectedMessage_(InputDevice* j) {
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
newly_disconnected_controllers_.push_back(j->GetDeviceName() + " "
|
||||
@ -258,7 +257,7 @@ void Input::ShowStandardInputDeviceDisconnectedMessage(InputDevice* j) {
|
||||
g_base->logic->DeleteAppTimer(disconnect_print_timer_id_);
|
||||
}
|
||||
disconnect_print_timer_id_ = g_base->logic->NewAppTimer(
|
||||
250, false, NewLambdaRunnable([this] { AnnounceDisconnects(); }));
|
||||
250, false, NewLambdaRunnable([this] { AnnounceDisconnects_(); }));
|
||||
}
|
||||
|
||||
void Input::PushAddInputDeviceCall(InputDevice* input_device,
|
||||
@ -313,8 +312,8 @@ void Input::AddInputDevice(InputDevice* device, bool standard_message) {
|
||||
// or something, but if it doesn't and thus matches an already-existing one,
|
||||
// we tack an index on to it. that way we can at least uniquely address them
|
||||
// based off how many are connected.
|
||||
device->set_number(GetNewNumberedIdentifier(device->GetRawDeviceName(),
|
||||
device->GetDeviceIdentifier()));
|
||||
device->set_number(GetNewNumberedIdentifier_(device->GetRawDeviceName(),
|
||||
device->GetDeviceIdentifier()));
|
||||
|
||||
// Let the device know it's been added (for custom announcements, etc.)
|
||||
device->OnAdded();
|
||||
@ -327,7 +326,7 @@ void Input::AddInputDevice(InputDevice* device, bool standard_message) {
|
||||
|
||||
// Need to do this after updating controls, as some control settings can
|
||||
// affect things we count (such as whether start activates default button).
|
||||
UpdateInputDeviceCounts();
|
||||
UpdateInputDeviceCounts_();
|
||||
}
|
||||
|
||||
if (g_buildconfig.ostype_macos()) {
|
||||
@ -344,7 +343,7 @@ void Input::AddInputDevice(InputDevice* device, bool standard_message) {
|
||||
}
|
||||
|
||||
if (standard_message && !device->ShouldBeHiddenFromUser()) {
|
||||
ShowStandardInputDeviceConnectedMessage(device);
|
||||
ShowStandardInputDeviceConnectedMessage_(device);
|
||||
}
|
||||
}
|
||||
|
||||
@ -360,7 +359,7 @@ void Input::RemoveInputDevice(InputDevice* input, bool standard_message) {
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
if (standard_message && !input->ShouldBeHiddenFromUser()) {
|
||||
ShowStandardInputDeviceDisconnectedMessage(input);
|
||||
ShowStandardInputDeviceDisconnectedMessage_(input);
|
||||
}
|
||||
|
||||
// Just look for it in our list.. if we find it, simply clear the ref
|
||||
@ -380,14 +379,14 @@ void Input::RemoveInputDevice(InputDevice* input, bool standard_message) {
|
||||
|
||||
// This should kill the device.
|
||||
device.Clear();
|
||||
UpdateInputDeviceCounts();
|
||||
UpdateInputDeviceCounts_();
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw Exception("Input::RemoveInputDevice: invalid device provided");
|
||||
}
|
||||
|
||||
void Input::UpdateInputDeviceCounts() {
|
||||
void Input::UpdateInputDeviceCounts_() {
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
auto current_time_millisecs =
|
||||
@ -528,7 +527,7 @@ auto Input::ShouldCompletelyIgnoreInputDevice(InputDevice* input_device)
|
||||
return ignore_sdl_controllers_ && input_device->IsSDLController();
|
||||
}
|
||||
|
||||
void Input::UpdateEnabledControllerSubsystems() {
|
||||
void Input::UpdateEnabledControllerSubsystems_() {
|
||||
assert(g_base);
|
||||
|
||||
// First off, on mac, let's update whether we want to completely ignore
|
||||
@ -566,7 +565,7 @@ void Input::OnAppShutdownComplete() { assert(g_base->InLogicThread()); }
|
||||
void Input::DoApplyAppConfig() {
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
UpdateEnabledControllerSubsystems();
|
||||
UpdateEnabledControllerSubsystems_();
|
||||
|
||||
// It's technically possible that updating these controls will add or
|
||||
// remove devices, thus changing the input_devices_ list, so lets work
|
||||
@ -579,7 +578,7 @@ void Input::DoApplyAppConfig() {
|
||||
}
|
||||
|
||||
// Some config settings can affect this.
|
||||
UpdateInputDeviceCounts();
|
||||
UpdateInputDeviceCounts_();
|
||||
}
|
||||
|
||||
void Input::OnScreenSizeChange() { assert(g_base->InLogicThread()); }
|
||||
@ -595,7 +594,7 @@ void Input::StepDisplayTime() {
|
||||
Log(LogLevel::kError,
|
||||
"Input has been temp-locked for 10 seconds; unlocking.");
|
||||
input_lock_count_temp_ = 0;
|
||||
PrintLockLabels();
|
||||
PrintLockLabels_();
|
||||
input_lock_temp_labels_.clear();
|
||||
input_unlock_temp_labels_.clear();
|
||||
}
|
||||
@ -610,7 +609,7 @@ void Input::StepDisplayTime() {
|
||||
// now.
|
||||
millisecs_t incr = 249;
|
||||
if (real_time - last_input_device_count_update_time_ > incr) {
|
||||
UpdateInputDeviceCounts();
|
||||
UpdateInputDeviceCounts_();
|
||||
last_input_device_count_update_time_ = real_time;
|
||||
|
||||
// Keep our idle-time up to date.
|
||||
@ -682,7 +681,7 @@ void Input::UnlockAllInput(bool permanent, const std::string& label) {
|
||||
input_unlock_permanent_labels_.push_back(label);
|
||||
if (input_lock_count_permanent_ < 0) {
|
||||
BA_LOG_PYTHON_TRACE_ONCE("lock-count-permanent < 0");
|
||||
PrintLockLabels();
|
||||
PrintLockLabels_();
|
||||
input_lock_count_permanent_ = 0;
|
||||
}
|
||||
|
||||
@ -713,7 +712,7 @@ void Input::UnlockAllInput(bool permanent, const std::string& label) {
|
||||
}
|
||||
}
|
||||
|
||||
void Input::PrintLockLabels() {
|
||||
void Input::PrintLockLabels_() {
|
||||
std::string s = "INPUT LOCK REPORT (time="
|
||||
+ std::to_string(g_core->GetAppTimeMillisecs()) + "):";
|
||||
int num;
|
||||
@ -827,12 +826,12 @@ void Input::PushJoystickEvent(const SDL_Event& event,
|
||||
InputDevice* input_device) {
|
||||
assert(g_base->logic->event_loop());
|
||||
g_base->logic->event_loop()->PushCall([this, event, input_device] {
|
||||
HandleJoystickEvent(event, input_device);
|
||||
HandleJoystickEvent_(event, input_device);
|
||||
});
|
||||
}
|
||||
|
||||
void Input::HandleJoystickEvent(const SDL_Event& event,
|
||||
InputDevice* input_device) {
|
||||
void Input::HandleJoystickEvent_(const SDL_Event& event,
|
||||
InputDevice* input_device) {
|
||||
assert(g_base->InLogicThread());
|
||||
assert(input_device);
|
||||
|
||||
@ -859,16 +858,28 @@ void Input::HandleJoystickEvent(const SDL_Event& event,
|
||||
input_device->HandleSDLEvent(&event);
|
||||
}
|
||||
|
||||
void Input::PushKeyPressEventSimple(int key) {
|
||||
assert(g_base->logic->event_loop());
|
||||
g_base->logic->event_loop()->PushCall(
|
||||
[this, key] { HandleKeyPressSimple_(key); });
|
||||
}
|
||||
|
||||
void Input::PushKeyReleaseEventSimple(int key) {
|
||||
assert(g_base->logic->event_loop());
|
||||
g_base->logic->event_loop()->PushCall(
|
||||
[this, key] { HandleKeyReleaseSimple_(key); });
|
||||
}
|
||||
|
||||
void Input::PushKeyPressEvent(const SDL_Keysym& keysym) {
|
||||
assert(g_base->logic->event_loop());
|
||||
g_base->logic->event_loop()->PushCall(
|
||||
[this, keysym] { HandleKeyPress(&keysym); });
|
||||
[this, keysym] { HandleKeyPress_(keysym); });
|
||||
}
|
||||
|
||||
void Input::PushKeyReleaseEvent(const SDL_Keysym& keysym) {
|
||||
assert(g_base->logic->event_loop());
|
||||
g_base->logic->event_loop()->PushCall(
|
||||
[this, keysym] { HandleKeyRelease(&keysym); });
|
||||
[this, keysym] { HandleKeyRelease_(keysym); });
|
||||
}
|
||||
|
||||
void Input::CaptureKeyboardInput(HandleKeyPressCall* press_call,
|
||||
@ -900,7 +911,41 @@ void Input::ReleaseJoystickInput() {
|
||||
joystick_input_capture_ = nullptr;
|
||||
}
|
||||
|
||||
void Input::HandleKeyPress(const SDL_Keysym* keysym) {
|
||||
void Input::AddFakeMods_(SDL_Keysym* sym) {
|
||||
// In cases where we are only passed simple keycodes, we fill in modifiers
|
||||
// ourself by looking at currently held key states. This is less than
|
||||
// ideal because modifier key states can fall out of sync in some cases
|
||||
// but is generally 'good enough' for our minimal keyboard needs.
|
||||
if (keys_held_.contains(SDLK_LCTRL) || keys_held_.contains(SDLK_RCTRL)) {
|
||||
sym->mod |= KMOD_CTRL;
|
||||
}
|
||||
if (keys_held_.contains(SDLK_LSHIFT) || keys_held_.contains(SDLK_RSHIFT)) {
|
||||
sym->mod |= KMOD_SHIFT;
|
||||
}
|
||||
if (keys_held_.contains(SDLK_LALT) || keys_held_.contains(SDLK_RALT)) {
|
||||
sym->mod |= KMOD_ALT;
|
||||
}
|
||||
if (keys_held_.contains(SDLK_LGUI) || keys_held_.contains(SDLK_RGUI)) {
|
||||
sym->mod |= KMOD_GUI;
|
||||
}
|
||||
}
|
||||
|
||||
void Input::HandleKeyPressSimple_(int keycode) {
|
||||
SDL_Keysym keysym{};
|
||||
keysym.sym = keycode;
|
||||
AddFakeMods_(&keysym);
|
||||
HandleKeyPress_(keysym);
|
||||
}
|
||||
|
||||
void Input::HandleKeyReleaseSimple_(int keycode) {
|
||||
// See notes above.
|
||||
SDL_Keysym keysym{};
|
||||
keysym.sym = keycode;
|
||||
AddFakeMods_(&keysym);
|
||||
HandleKeyRelease_(keysym);
|
||||
}
|
||||
|
||||
void Input::HandleKeyPress_(const SDL_Keysym& keysym) {
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
MarkInputActive();
|
||||
@ -912,7 +957,7 @@ void Input::HandleKeyPress(const SDL_Keysym* keysym) {
|
||||
|
||||
// If someone is capturing these events, give them a crack at it.
|
||||
if (keyboard_input_capture_press_) {
|
||||
if (keyboard_input_capture_press_(*keysym)) {
|
||||
if (keyboard_input_capture_press_(keysym)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -920,19 +965,19 @@ void Input::HandleKeyPress(const SDL_Keysym* keysym) {
|
||||
// Regardless of what else we do, keep track of mod key states.
|
||||
// (for things like manual camera moves. For individual key presses
|
||||
// ideally we should use the modifiers bundled with the key presses)
|
||||
UpdateModKeyStates(keysym, true);
|
||||
UpdateModKeyStates_(&keysym, true);
|
||||
|
||||
bool repeat_press;
|
||||
if (keys_held_.count(keysym->sym) != 0) {
|
||||
if (keys_held_.count(keysym.sym) != 0) {
|
||||
repeat_press = true;
|
||||
} else {
|
||||
repeat_press = false;
|
||||
keys_held_.insert(keysym->sym);
|
||||
keys_held_.insert(keysym.sym);
|
||||
}
|
||||
|
||||
// Mobile-specific stuff.
|
||||
if (g_buildconfig.ostype_ios_tvos() || g_buildconfig.ostype_android()) {
|
||||
switch (keysym->sym) {
|
||||
switch (keysym.sym) {
|
||||
// FIXME: See if this stuff is still necessary. Was this perhaps
|
||||
// specifically to support the console?
|
||||
case SDLK_DELETE:
|
||||
@ -944,7 +989,7 @@ void Input::HandleKeyPress(const SDL_Keysym* keysym) {
|
||||
// them a TEXT_INPUT message with no string.. I made them resistant
|
||||
// to that case but wondering if we can take this out?
|
||||
g_base->ui->SendWidgetMessage(
|
||||
WidgetMessage(WidgetMessage::Type::kTextInput, keysym));
|
||||
WidgetMessage(WidgetMessage::Type::kTextInput, &keysym));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -954,8 +999,8 @@ void Input::HandleKeyPress(const SDL_Keysym* keysym) {
|
||||
|
||||
// Command-F or Control-F toggles full-screen if the app-adapter supports it.
|
||||
if (g_base->app_adapter->CanToggleFullscreen()) {
|
||||
if (!repeat_press && keysym->sym == SDLK_f
|
||||
&& ((keysym->mod & KMOD_CTRL) || (keysym->mod & KMOD_GUI))) {
|
||||
if (!repeat_press && keysym.sym == SDLK_f
|
||||
&& ((keysym.mod & KMOD_CTRL) || (keysym.mod & KMOD_GUI))) {
|
||||
g_base->python->objs()
|
||||
.Get(BasePython::ObjID::kToggleFullscreenCall)
|
||||
.Call();
|
||||
@ -963,24 +1008,24 @@ void Input::HandleKeyPress(const SDL_Keysym* keysym) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
// Control-Q quits. On Mac, the usual Cmd-Q gets handled
|
||||
// by the app-adapter implicitly.
|
||||
if (!repeat_press && keysym.sym == SDLK_q && (keysym.mod & KMOD_CTRL)) {
|
||||
g_base->QuitApp(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Let the console intercept stuff if it wants at this point.
|
||||
if (auto* console = g_base->ui->dev_console()) {
|
||||
if (console->HandleKeyPress(keysym)) {
|
||||
if (console->HandleKeyPress(&keysym)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 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))) {
|
||||
if (!repeat_press && keysym.sym == SDLK_v
|
||||
&& ((keysym.mod & KMOD_CTRL) || (keysym.mod & KMOD_GUI))) {
|
||||
g_base->ui->SendWidgetMessage(WidgetMessage(WidgetMessage::Type::kPaste));
|
||||
return;
|
||||
}
|
||||
@ -989,7 +1034,7 @@ void Input::HandleKeyPress(const SDL_Keysym* keysym) {
|
||||
|
||||
// None of the following stuff accepts key repeats.
|
||||
if (!repeat_press) {
|
||||
switch (keysym->sym) {
|
||||
switch (keysym.sym) {
|
||||
// Menu button on android/etc. pops up the menu.
|
||||
case SDLK_MENU: {
|
||||
if (!g_base->ui->MainMenuVisible()) {
|
||||
@ -1001,14 +1046,14 @@ void Input::HandleKeyPress(const SDL_Keysym* keysym) {
|
||||
|
||||
case SDLK_EQUALS:
|
||||
case SDLK_PLUS:
|
||||
if (keysym->mod & KMOD_CTRL) {
|
||||
if (keysym.mod & KMOD_CTRL) {
|
||||
g_base->app_mode()->ChangeGameSpeed(1);
|
||||
handled = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_MINUS:
|
||||
if (keysym->mod & KMOD_CTRL) {
|
||||
if (keysym.mod & KMOD_CTRL) {
|
||||
g_base->app_mode()->ChangeGameSpeed(-1);
|
||||
handled = true;
|
||||
}
|
||||
@ -1073,12 +1118,12 @@ void Input::HandleKeyPress(const SDL_Keysym* keysym) {
|
||||
// If we haven't claimed it, pass it along as potential player/widget input.
|
||||
if (!handled) {
|
||||
if (keyboard_input_) {
|
||||
keyboard_input_->HandleKey(keysym, repeat_press, true);
|
||||
keyboard_input_->HandleKey(&keysym, repeat_press, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Input::HandleKeyRelease(const SDL_Keysym* keysym) {
|
||||
void Input::HandleKeyRelease_(const SDL_Keysym& keysym) {
|
||||
assert(g_base);
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
@ -1089,32 +1134,32 @@ void Input::HandleKeyRelease(const SDL_Keysym* keysym) {
|
||||
// In some cases we may receive duplicate key-release events (if a
|
||||
// keyboard reset was run, it deals out key releases, but then the
|
||||
// keyboard driver issues them as well).
|
||||
if (keys_held_.count(keysym->sym) == 0) {
|
||||
if (keys_held_.count(keysym.sym) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If someone is capturing these events, give them a crack at it.
|
||||
if (keyboard_input_capture_release_) {
|
||||
(keyboard_input_capture_release_(*keysym));
|
||||
(keyboard_input_capture_release_(keysym));
|
||||
}
|
||||
|
||||
// Keep track of mod key states for things like manual camera moves. For
|
||||
// individual key presses ideally we should instead use modifiers bundled
|
||||
// with the key press events.
|
||||
UpdateModKeyStates(keysym, false);
|
||||
UpdateModKeyStates_(&keysym, false);
|
||||
|
||||
keys_held_.erase(keysym->sym);
|
||||
keys_held_.erase(keysym.sym);
|
||||
|
||||
if (g_base->ui->dev_console() != nullptr) {
|
||||
g_base->ui->dev_console()->HandleKeyRelease(keysym);
|
||||
g_base->ui->dev_console()->HandleKeyRelease(&keysym);
|
||||
}
|
||||
|
||||
if (keyboard_input_) {
|
||||
keyboard_input_->HandleKey(keysym, false, false);
|
||||
keyboard_input_->HandleKey(&keysym, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
void Input::UpdateModKeyStates(const SDL_Keysym* keysym, bool press) {
|
||||
void Input::UpdateModKeyStates_(const SDL_Keysym* keysym, bool press) {
|
||||
switch (keysym->sym) {
|
||||
case SDLK_LCTRL:
|
||||
case SDLK_RCTRL: {
|
||||
@ -1145,10 +1190,10 @@ void Input::UpdateModKeyStates(const SDL_Keysym* keysym, bool press) {
|
||||
void Input::PushMouseScrollEvent(const Vector2f& amount) {
|
||||
assert(g_base->logic->event_loop());
|
||||
g_base->logic->event_loop()->PushCall(
|
||||
[this, amount] { HandleMouseScroll(amount); });
|
||||
[this, amount] { HandleMouseScroll_(amount); });
|
||||
}
|
||||
|
||||
void Input::HandleMouseScroll(const Vector2f& amount) {
|
||||
void Input::HandleMouseScroll_(const Vector2f& amount) {
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
// If input is locked, allow it to mark us active but nothing more.
|
||||
@ -1181,11 +1226,11 @@ void Input::PushSmoothMouseScrollEvent(const Vector2f& velocity,
|
||||
bool momentum) {
|
||||
assert(g_base->logic->event_loop());
|
||||
g_base->logic->event_loop()->PushCall([this, velocity, momentum] {
|
||||
HandleSmoothMouseScroll(velocity, momentum);
|
||||
HandleSmoothMouseScroll_(velocity, momentum);
|
||||
});
|
||||
}
|
||||
|
||||
void Input::HandleSmoothMouseScroll(const Vector2f& velocity, bool momentum) {
|
||||
void Input::HandleSmoothMouseScroll_(const Vector2f& velocity, bool momentum) {
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
// If input is locked, allow it to mark us active but nothing more.
|
||||
@ -1216,10 +1261,10 @@ void Input::HandleSmoothMouseScroll(const Vector2f& velocity, bool momentum) {
|
||||
void Input::PushMouseMotionEvent(const Vector2f& position) {
|
||||
assert(g_base->logic->event_loop());
|
||||
g_base->logic->event_loop()->PushCall(
|
||||
[this, position] { HandleMouseMotion(position); });
|
||||
[this, position] { HandleMouseMotion_(position); });
|
||||
}
|
||||
|
||||
void Input::HandleMouseMotion(const Vector2f& position) {
|
||||
void Input::HandleMouseMotion_(const Vector2f& position) {
|
||||
assert(g_base);
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
@ -1266,10 +1311,10 @@ void Input::HandleMouseMotion(const Vector2f& position) {
|
||||
void Input::PushMouseDownEvent(int button, const Vector2f& position) {
|
||||
assert(g_base->logic->event_loop());
|
||||
g_base->logic->event_loop()->PushCall(
|
||||
[this, button, position] { HandleMouseDown(button, position); });
|
||||
[this, button, position] { HandleMouseDown_(button, position); });
|
||||
}
|
||||
|
||||
void Input::HandleMouseDown(int button, const Vector2f& position) {
|
||||
void Input::HandleMouseDown_(int button, const Vector2f& position) {
|
||||
assert(g_base);
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
@ -1330,10 +1375,10 @@ void Input::HandleMouseDown(int button, const Vector2f& position) {
|
||||
void Input::PushMouseUpEvent(int button, const Vector2f& position) {
|
||||
assert(g_base->logic->event_loop());
|
||||
g_base->logic->event_loop()->PushCall(
|
||||
[this, button, position] { HandleMouseUp(button, position); });
|
||||
[this, button, position] { HandleMouseUp_(button, position); });
|
||||
}
|
||||
|
||||
void Input::HandleMouseUp(int button, const Vector2f& position) {
|
||||
void Input::HandleMouseUp_(int button, const Vector2f& position) {
|
||||
assert(g_base->InLogicThread());
|
||||
MarkInputActive();
|
||||
|
||||
@ -1373,10 +1418,10 @@ void Input::HandleMouseUp(int button, const Vector2f& position) {
|
||||
|
||||
void Input::PushTouchEvent(const TouchEvent& e) {
|
||||
assert(g_base->logic->event_loop());
|
||||
g_base->logic->event_loop()->PushCall([e, this] { HandleTouchEvent(e); });
|
||||
g_base->logic->event_loop()->PushCall([e, this] { HandleTouchEvent_(e); });
|
||||
}
|
||||
|
||||
void Input::HandleTouchEvent(const TouchEvent& e) {
|
||||
void Input::HandleTouchEvent_(const TouchEvent& e) {
|
||||
assert(g_base->InLogicThread());
|
||||
assert(g_base->graphics);
|
||||
|
||||
@ -1422,11 +1467,11 @@ void Input::HandleTouchEvent(const TouchEvent& e) {
|
||||
// mouse events which covers most UI stuff.
|
||||
if (e.type == TouchEvent::Type::kDown && single_touch_ == nullptr) {
|
||||
single_touch_ = e.touch;
|
||||
HandleMouseDown(SDL_BUTTON_LEFT, Vector2f(e.x, e.y));
|
||||
HandleMouseDown_(SDL_BUTTON_LEFT, Vector2f(e.x, e.y));
|
||||
}
|
||||
|
||||
if (e.type == TouchEvent::Type::kMoved && e.touch == single_touch_) {
|
||||
HandleMouseMotion(Vector2f(e.x, e.y));
|
||||
HandleMouseMotion_(Vector2f(e.x, e.y));
|
||||
}
|
||||
|
||||
// Currently just applying touch-cancel the same as touch-up here;
|
||||
@ -1434,7 +1479,7 @@ void Input::HandleTouchEvent(const TouchEvent& e) {
|
||||
if ((e.type == TouchEvent::Type::kUp || e.type == TouchEvent::Type::kCanceled)
|
||||
&& (e.touch == single_touch_ || e.overall)) {
|
||||
single_touch_ = nullptr;
|
||||
HandleMouseUp(SDL_BUTTON_LEFT, Vector2f(e.x, e.y));
|
||||
HandleMouseUp_(SDL_BUTTON_LEFT, Vector2f(e.x, e.y));
|
||||
}
|
||||
|
||||
// If we've got a touch input device, forward events along to it.
|
||||
@ -1460,7 +1505,7 @@ void Input::ResetKeyboardHeldKeys() {
|
||||
SDL_Keysym k;
|
||||
memset(&k, 0, sizeof(k));
|
||||
k.sym = (SDL_Keycode)(*keys_held_.begin());
|
||||
HandleKeyRelease(&k);
|
||||
HandleKeyRelease_(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,6 +119,8 @@ class Input {
|
||||
void CreateTouchInput();
|
||||
|
||||
void PushTextInputEvent(const std::string& text);
|
||||
void PushKeyPressEventSimple(int keycode);
|
||||
void PushKeyReleaseEventSimple(int keycode);
|
||||
void PushKeyPressEvent(const SDL_Keysym& keysym);
|
||||
void PushKeyReleaseEvent(const SDL_Keysym& keysym);
|
||||
void PushMouseDownEvent(int button, const Vector2f& position);
|
||||
@ -152,27 +154,30 @@ class Input {
|
||||
void RebuildInputDeviceDelegates();
|
||||
|
||||
private:
|
||||
void UpdateInputDeviceCounts();
|
||||
auto GetNewNumberedIdentifier(const std::string& name,
|
||||
const std::string& identifier) -> int;
|
||||
void UpdateEnabledControllerSubsystems();
|
||||
void AnnounceConnects();
|
||||
void AnnounceDisconnects();
|
||||
void HandleKeyPress(const SDL_Keysym* keysym);
|
||||
void HandleKeyRelease(const SDL_Keysym* keysym);
|
||||
void HandleMouseMotion(const Vector2f& position);
|
||||
void HandleMouseDown(int button, const Vector2f& position);
|
||||
void HandleMouseUp(int button, const Vector2f& position);
|
||||
void HandleMouseScroll(const Vector2f& amount);
|
||||
void HandleSmoothMouseScroll(const Vector2f& velocity, bool momentum);
|
||||
void HandleJoystickEvent(const SDL_Event& event, InputDevice* input_device);
|
||||
void HandleTouchEvent(const TouchEvent& e);
|
||||
void ShowStandardInputDeviceConnectedMessage(InputDevice* j);
|
||||
void ShowStandardInputDeviceDisconnectedMessage(InputDevice* j);
|
||||
void PrintLockLabels();
|
||||
void UpdateModKeyStates(const SDL_Keysym* keysym, bool press);
|
||||
void CreateKeyboardInputDevices();
|
||||
void DestroyKeyboardInputDevices();
|
||||
void UpdateInputDeviceCounts_();
|
||||
auto GetNewNumberedIdentifier_(const std::string& name,
|
||||
const std::string& identifier) -> int;
|
||||
void UpdateEnabledControllerSubsystems_();
|
||||
void AnnounceConnects_();
|
||||
void AnnounceDisconnects_();
|
||||
void HandleKeyPressSimple_(int keycode);
|
||||
void HandleKeyReleaseSimple_(int keycode);
|
||||
void HandleKeyPress_(const SDL_Keysym& keysym);
|
||||
void HandleKeyRelease_(const SDL_Keysym& keysym);
|
||||
void HandleMouseMotion_(const Vector2f& position);
|
||||
void HandleMouseDown_(int button, const Vector2f& position);
|
||||
void HandleMouseUp_(int button, const Vector2f& position);
|
||||
void HandleMouseScroll_(const Vector2f& amount);
|
||||
void HandleSmoothMouseScroll_(const Vector2f& velocity, bool momentum);
|
||||
void HandleJoystickEvent_(const SDL_Event& event, InputDevice* input_device);
|
||||
void HandleTouchEvent_(const TouchEvent& e);
|
||||
void ShowStandardInputDeviceConnectedMessage_(InputDevice* j);
|
||||
void ShowStandardInputDeviceDisconnectedMessage_(InputDevice* j);
|
||||
void PrintLockLabels_();
|
||||
void UpdateModKeyStates_(const SDL_Keysym* keysym, bool press);
|
||||
void CreateKeyboardInputDevices_();
|
||||
void DestroyKeyboardInputDevices_();
|
||||
void AddFakeMods_(SDL_Keysym* sym);
|
||||
|
||||
HandleKeyPressCall* keyboard_input_capture_press_{};
|
||||
HandleKeyReleaseCall* keyboard_input_capture_release_{};
|
||||
|
||||
@ -29,7 +29,7 @@ Logic::Logic() : display_timers_(new TimerList()) {
|
||||
void Logic::OnMainThreadStartApp() {
|
||||
// Spin up our logic thread and sit and wait for it to init.
|
||||
event_loop_ = new EventLoop(EventLoopID::kLogic);
|
||||
g_core->pausable_event_loops.push_back(event_loop_);
|
||||
g_core->suspendable_event_loops.push_back(event_loop_);
|
||||
event_loop_->PushCallSynchronous([this] { OnAppStart(); });
|
||||
}
|
||||
|
||||
@ -47,9 +47,9 @@ void Logic::OnAppStart() {
|
||||
event_loop_->SetAcquiresPythonGIL();
|
||||
|
||||
// Stay informed when our event loop is pausing/unpausing.
|
||||
event_loop_->AddPauseCallback(
|
||||
event_loop_->AddSuspendCallback(
|
||||
NewLambdaRunnableUnmanaged([this] { OnAppPause(); }));
|
||||
event_loop_->AddResumeCallback(
|
||||
event_loop_->AddUnsuspendCallback(
|
||||
NewLambdaRunnableUnmanaged([this] { OnAppResume(); }));
|
||||
|
||||
// Running in a specific order here and should try to stick to it in
|
||||
@ -454,7 +454,7 @@ void Logic::UpdateDisplayTimeForFrameDraw() {
|
||||
// look like the game is slowing down or speeding up.
|
||||
|
||||
// Flip debug-log-display-time on to debug this stuff.
|
||||
// Things to look for:'
|
||||
// Things to look for:
|
||||
// - 'final' value should mostly stay constant.
|
||||
// - 'final' value should not be *too* far from 'current'.
|
||||
// - 'current' should mostly show '(avg)'; rarely '(sample)'.
|
||||
@ -484,51 +484,16 @@ void Logic::UpdateDisplayTimeForFrameDraw() {
|
||||
(recent_display_time_increments_index_ + 1) % kDisplayTimeSampleCount;
|
||||
}
|
||||
|
||||
// It seems that when things get thrown off it is often due to a single
|
||||
// rogue sample being unusually long and often the next one being
|
||||
// unusually short. Let's try to filter out some of these cases by
|
||||
// ignoring both the longest and shortest sample in our set.
|
||||
//
|
||||
// SCRATCH THAT; we want to gracefully handle cases where vsync combined
|
||||
// with target-framerate might give us crazy sample times like 5, 10, 5,
|
||||
// 10, etc. So we can't expect filtering a single sample to help.
|
||||
//
|
||||
// int max_index{};
|
||||
// int min_index{};
|
||||
// double max_val{recent_display_time_increments_[0]};
|
||||
// double min_val{recent_display_time_increments_[0]};
|
||||
// for (int i = 0; i < kDisplayTimeSampleCount; ++i) {
|
||||
// auto val = recent_display_time_increments_[i];
|
||||
// if (val > max_val) {
|
||||
// max_val = val;
|
||||
// max_index = i;
|
||||
// }
|
||||
// if (val < min_val) {
|
||||
// min_val = val;
|
||||
// min_index = i;
|
||||
// }
|
||||
// }
|
||||
|
||||
double avg{};
|
||||
double min{};
|
||||
double max{};
|
||||
int count{};
|
||||
double min, max;
|
||||
min = max = recent_display_time_increments_[0];
|
||||
for (int i = 0; i < kDisplayTimeSampleCount; ++i) {
|
||||
// if (i == min_index || i == max_index) {
|
||||
// continue;
|
||||
// }
|
||||
auto val = recent_display_time_increments_[i];
|
||||
if (count == 0) {
|
||||
// We may have skipped first index(es) so need to do this here
|
||||
// instead of initing min/max to first value.
|
||||
min = max = val;
|
||||
}
|
||||
avg += val;
|
||||
min = std::min(min, val);
|
||||
max = std::max(max, val);
|
||||
count += 1;
|
||||
}
|
||||
avg /= count;
|
||||
avg /= kDisplayTimeSampleCount;
|
||||
double range = max - min;
|
||||
|
||||
// If our range of recent increment values is somewhat large relative to
|
||||
@ -541,19 +506,16 @@ void Logic::UpdateDisplayTimeForFrameDraw() {
|
||||
// the lower chaos will be and thus the more the engine will stick to
|
||||
// smoothed values. A good way to determine if this value is too high is
|
||||
// to launch the game and watch the menu animation. If it visibly speeds
|
||||
// up or slows down in the moment after launch, it means the value is
|
||||
// too high and the engine is sticking with smoothed values when it
|
||||
// should instead be reacting immediately. So basically this value
|
||||
// should be as high as possible while avoiding that look.
|
||||
// up or slows down in a 'rubber band' looking way the moment after
|
||||
// launch, it means the value is too high and the engine is sticking
|
||||
// with smoothed values when it should instead be reacting immediately.
|
||||
// So basically this value should be as high as possible while avoiding
|
||||
// that look.
|
||||
double chaos_fudge{1.25};
|
||||
double chaos = (range / avg) / chaos_fudge;
|
||||
bool use_avg = chaos < 1.0;
|
||||
auto used = use_avg ? avg : this_increment;
|
||||
|
||||
// double chaos = 0.0;
|
||||
// bool use_avg = true;
|
||||
// auto used = use_avg ? avg : this_increment;
|
||||
|
||||
// Lastly use this 'used' value to update our actual increment - our
|
||||
// increment moves only if 'used' value gets farther than [trail_buffer]
|
||||
// from it. So ideally it will sit in the middle of the smoothed value
|
||||
@ -595,6 +557,7 @@ void Logic::UpdateDisplayTimeForFrameDraw() {
|
||||
Log(LogLevel::kDebug, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
// Lastly, apply our updated increment value to our time.
|
||||
display_time_ += display_time_increment_;
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ NetworkWriter::NetworkWriter() {}
|
||||
void NetworkWriter::OnMainThreadStartApp() {
|
||||
// Spin up our thread.
|
||||
event_loop_ = new EventLoop(EventLoopID::kNetworkWrite);
|
||||
g_core->pausable_event_loops.push_back(event_loop_);
|
||||
g_core->suspendable_event_loops.push_back(event_loop_);
|
||||
}
|
||||
|
||||
void NetworkWriter::PushSendToCall(const std::vector<uint8_t>& msg,
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
#include "ballistica/base/app_adapter/app_adapter.h"
|
||||
#include "ballistica/base/networking/network_reader.h"
|
||||
#include "ballistica/base/support/app_config.h"
|
||||
#include "ballistica/shared/foundation/event_loop.h"
|
||||
#include "ballistica/shared/networking/sockaddr.h"
|
||||
|
||||
namespace ballistica::base {
|
||||
|
||||
@ -62,14 +62,6 @@ void BasePlatformApple::DoOpenURL(const std::string& url) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void BasePlatformApple::TerminateApp() {
|
||||
#if BA_OSTYPE_MACOS && BA_XCODE_BUILD && !BA_HEADLESS_BUILD
|
||||
AppleUtils::TerminateApp();
|
||||
#else
|
||||
BasePlatform::TerminateApp();
|
||||
#endif
|
||||
}
|
||||
|
||||
// void BasePlatformApple::SetHardwareCursorVisible(bool visible) {
|
||||
// // Set our nifty custom hardware cursor on mac;
|
||||
// // otherwise fall back to default.
|
||||
|
||||
@ -16,12 +16,9 @@ class BasePlatformApple : public BasePlatform {
|
||||
void RestorePurchases() override;
|
||||
void PurchaseAck(const std::string& purchase,
|
||||
const std::string& order_id) override;
|
||||
void TerminateApp() override;
|
||||
|
||||
void DoOpenURL(const std::string& url) override;
|
||||
|
||||
// void SetHardwareCursorVisible(bool visible) override;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
|
||||
@ -245,20 +245,6 @@ void BasePlatform::SetupInterruptHandling() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void BasePlatform::GetCursorPosition(float* x, float* y) {
|
||||
assert(x && y);
|
||||
|
||||
// By default, just use our latest event-delivered cursor position;
|
||||
// this should work everywhere though perhaps might not be most optimal.
|
||||
if (g_base->input == nullptr) {
|
||||
*x = 0.0f;
|
||||
*y = 0.0f;
|
||||
return;
|
||||
}
|
||||
*x = g_base->input->cursor_pos_x();
|
||||
*y = g_base->input->cursor_pos_y();
|
||||
}
|
||||
|
||||
void BasePlatform::OnMainThreadStartAppComplete() {}
|
||||
|
||||
void BasePlatform::OnAppStart() { assert(g_base->InLogicThread()); }
|
||||
@ -269,13 +255,6 @@ void BasePlatform::OnAppShutdownComplete() { assert(g_base->InLogicThread()); }
|
||||
void BasePlatform::OnScreenSizeChange() { assert(g_base->InLogicThread()); }
|
||||
void BasePlatform::DoApplyAppConfig() { assert(g_base->InLogicThread()); }
|
||||
|
||||
void BasePlatform::TerminateApp() { exit(g_base->return_value()); }
|
||||
|
||||
auto BasePlatform::CanSoftQuit() -> bool { return false; }
|
||||
auto BasePlatform::CanBackQuit() -> bool { return false; }
|
||||
void BasePlatform::DoBackQuit() {}
|
||||
void BasePlatform::DoSoftQuit() {}
|
||||
|
||||
auto BasePlatform::HaveStringEditor() -> bool { return false; }
|
||||
|
||||
void BasePlatform::InvokeStringEditor(PyObject* string_edit_adapter) {
|
||||
@ -322,11 +301,4 @@ void BasePlatform::DoInvokeStringEditor(const std::string& title,
|
||||
Log(LogLevel::kError, "FIXME: DoInvokeStringEditor() unimplemented");
|
||||
}
|
||||
|
||||
void BasePlatform::SetHardwareCursorVisible(bool visible) {
|
||||
// FIXME: Move/forward this to AppAdapter.
|
||||
#if BA_SDL_BUILD
|
||||
SDL_ShowCursor(visible ? SDL_ENABLE : SDL_DISABLE);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
@ -33,36 +33,6 @@ class BasePlatform {
|
||||
virtual void OnScreenSizeChange();
|
||||
virtual void DoApplyAppConfig();
|
||||
|
||||
/// 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.
|
||||
virtual auto CanSoftQuit() -> bool;
|
||||
|
||||
/// Implement soft-quit behavior. Will always be called in the logic
|
||||
/// thread. Make sure to also override CanBackQuit to reflect this being
|
||||
/// present. Note that when quitting the app yourself, you should use
|
||||
/// g_base->QuitApp(); do not call this directly.
|
||||
virtual void DoSoftQuit();
|
||||
|
||||
/// Return whether this platform supports back-quit. A back quit is a
|
||||
/// variation of soft-quit generally triggered by a back button, which may
|
||||
/// give different results in the OS. For example on Android this may
|
||||
/// result in jumping back to the previous Android activity instead of
|
||||
/// just ending the current one and dumping to the home screen as normal
|
||||
/// soft quit might do.
|
||||
virtual auto CanBackQuit() -> bool;
|
||||
|
||||
/// Implement back-quit behavior. Will always be called in the logic
|
||||
/// thread. Make sure to also override CanBackQuit to reflect this being
|
||||
/// present. Note that when quitting the app yourself, you should use
|
||||
/// g_base->QuitApp(); do not call this directly.
|
||||
virtual void DoBackQuit();
|
||||
|
||||
/// Terminate the app. This can be immediate or by posting some high
|
||||
/// level event. There should be nothing left to do in the engine at
|
||||
/// this point.
|
||||
virtual void TerminateApp();
|
||||
|
||||
#pragma mark IN APP PURCHASES --------------------------------------------------
|
||||
|
||||
void Purchase(const std::string& item);
|
||||
@ -116,12 +86,6 @@ class BasePlatform {
|
||||
/// Open the provided URL in a browser or whatnot.
|
||||
void OpenURL(const std::string& url);
|
||||
|
||||
/// Get the most up-to-date cursor position.
|
||||
void GetCursorPosition(float* x, float* y);
|
||||
|
||||
/// Show/hide the hardware cursor.
|
||||
virtual void SetHardwareCursorVisible(bool visible);
|
||||
|
||||
/// Should be called by platform StringEditor to apply a value.
|
||||
/// Must be called in the logic thread.
|
||||
void StringEditorApply(const std::string& val);
|
||||
|
||||
@ -103,20 +103,20 @@ void BasePython::SoftImportClassic() {
|
||||
}
|
||||
}
|
||||
|
||||
void BasePython::SoftImportUIV1() {
|
||||
// To keep our init order clean, we want to root out any attempted uses
|
||||
// of this before _babase/babase has been fully imported.
|
||||
assert(g_base);
|
||||
assert(g_base->IsBaseCompletelyImported());
|
||||
// void BasePython::SoftImportUIV1() {
|
||||
// // To keep our init order clean, we want to root out any attempted uses
|
||||
// // of this before _babase/babase has been fully imported.
|
||||
// assert(g_base);
|
||||
// assert(g_base->IsBaseCompletelyImported());
|
||||
|
||||
auto gil{Python::ScopedInterpreterLock()};
|
||||
auto result = PythonRef::StolenSoft(PyImport_ImportModule("_bauiv1"));
|
||||
if (!result.Exists()) {
|
||||
// Ignore any errors here for now. All that will matter is whether plus
|
||||
// gave us its interface.
|
||||
PyErr_Clear();
|
||||
}
|
||||
}
|
||||
// auto gil{Python::ScopedInterpreterLock()};
|
||||
// auto result = PythonRef::StolenSoft(PyImport_ImportModule("_bauiv1"));
|
||||
// if (!result.Exists()) {
|
||||
// // Ignore any errors here for now. All that will matter is whether plus
|
||||
// // gave us its interface.
|
||||
// PyErr_Clear();
|
||||
// }
|
||||
// }
|
||||
|
||||
void BasePython::ReadConfig() {
|
||||
auto gil{Python::ScopedInterpreterLock()};
|
||||
@ -151,12 +151,12 @@ void BasePython::OnAppStart() {
|
||||
|
||||
void BasePython::OnAppPause() {
|
||||
assert(g_base->InLogicThread());
|
||||
objs().Get(BasePython::ObjID::kAppOnNativePauseCall).Call();
|
||||
objs().Get(BasePython::ObjID::kAppOnNativeSuspendCall).Call();
|
||||
}
|
||||
|
||||
void BasePython::OnAppResume() {
|
||||
assert(g_base->InLogicThread());
|
||||
objs().Get(BasePython::ObjID::kAppOnNativeResumeCall).Call();
|
||||
objs().Get(BasePython::ObjID::kAppOnNativeUnsuspendCall).Call();
|
||||
}
|
||||
|
||||
void BasePython::OnAppShutdown() {
|
||||
@ -467,6 +467,10 @@ auto BasePython::GetPyEnum_TimeType(PyObject* obj) -> TimeType {
|
||||
return GetPyEnum<TimeType>(BasePython::ObjID::kTimeTypeClass, obj);
|
||||
}
|
||||
|
||||
auto BasePython::GetPyEnum_QuitType(PyObject* obj) -> QuitType {
|
||||
return GetPyEnum<QuitType>(BasePython::ObjID::kQuitTypeClass, obj);
|
||||
}
|
||||
|
||||
auto BasePython::GetPyEnum_TimeFormat(PyObject* obj) -> TimeFormat {
|
||||
return GetPyEnum<TimeFormat>(BasePython::ObjID::kTimeFormatClass, obj);
|
||||
}
|
||||
@ -479,6 +483,14 @@ auto BasePython::GetPyEnum_InputType(PyObject* obj) -> InputType {
|
||||
return GetPyEnum<InputType>(BasePython::ObjID::kInputTypeClass, obj);
|
||||
}
|
||||
|
||||
// TODO(ericf): Make this a template.
|
||||
auto BasePython::PyQuitType(QuitType val) -> PythonRef {
|
||||
auto args = PythonRef::Stolen(Py_BuildValue("(d)", static_cast<int>(val)));
|
||||
auto out = objs().Get(BasePython::ObjID::kQuitTypeClass).Call(args);
|
||||
BA_PRECONDITION(out.Exists());
|
||||
return out;
|
||||
}
|
||||
|
||||
auto BasePython::GetResource(const char* key, const char* fallback_resource,
|
||||
const char* fallback_value) -> std::string {
|
||||
assert(Python::HaveGIL());
|
||||
|
||||
@ -70,8 +70,8 @@ class BasePython {
|
||||
kUIRemotePressCall,
|
||||
kRemoveInGameAdsMessageCall,
|
||||
kAppOnNativeStartCall,
|
||||
kAppOnNativePauseCall,
|
||||
kAppOnNativeResumeCall,
|
||||
kAppOnNativeSuspendCall,
|
||||
kAppOnNativeUnsuspendCall,
|
||||
kAppOnNativeShutdownCall,
|
||||
kAppOnNativeShutdownCompleteCall,
|
||||
kQuitCall,
|
||||
@ -88,6 +88,7 @@ class BasePython {
|
||||
kSessionNotFoundError,
|
||||
kTimeFormatClass,
|
||||
kTimeTypeClass,
|
||||
kQuitTypeClass,
|
||||
kInputTypeClass,
|
||||
kPermissionClass,
|
||||
kSpecialCharClass,
|
||||
@ -151,6 +152,9 @@ class BasePython {
|
||||
static auto GetPyEnum_TimeFormat(PyObject* obj) -> TimeFormat;
|
||||
static auto IsPyEnum_InputType(PyObject* obj) -> bool;
|
||||
static auto GetPyEnum_InputType(PyObject* obj) -> InputType;
|
||||
static auto GetPyEnum_QuitType(PyObject* obj) -> QuitType;
|
||||
|
||||
auto PyQuitType(QuitType val) -> PythonRef;
|
||||
|
||||
auto CanPyStringEditAdapterBeReplaced(PyObject* o) -> bool;
|
||||
|
||||
@ -167,7 +171,7 @@ class BasePython {
|
||||
|
||||
void SoftImportPlus();
|
||||
void SoftImportClassic();
|
||||
void SoftImportUIV1();
|
||||
// void SoftImportUIV1();
|
||||
|
||||
private:
|
||||
std::set<std::string> do_once_locations_;
|
||||
|
||||
@ -6,13 +6,11 @@
|
||||
#include "ballistica/base/app_mode/app_mode_empty.h"
|
||||
#include "ballistica/base/graphics/graphics_server.h"
|
||||
#include "ballistica/base/logic/logic.h"
|
||||
#include "ballistica/base/platform/base_platform.h"
|
||||
#include "ballistica/base/python/base_python.h"
|
||||
#include "ballistica/base/python/support/python_context_call_runnable.h"
|
||||
#include "ballistica/base/support/stress_test.h"
|
||||
#include "ballistica/base/ui/dev_console.h"
|
||||
#include "ballistica/base/ui/ui.h"
|
||||
#include "ballistica/core/platform/core_platform.h"
|
||||
#include "ballistica/shared/foundation/event_loop.h"
|
||||
#include "ballistica/shared/foundation/logging.h"
|
||||
#include "ballistica/shared/python/python.h"
|
||||
@ -511,26 +509,20 @@ static auto PyQuit(PyObject* self, PyObject* args, PyObject* keywds)
|
||||
BA_PYTHON_TRY;
|
||||
BA_PRECONDITION(g_base->IsAppStarted());
|
||||
|
||||
static const char* kwlist[] = {"soft", "back", nullptr};
|
||||
int soft = 0;
|
||||
int back = 0;
|
||||
if (!PyArg_ParseTupleAndKeywords(args, keywds, "|pp",
|
||||
const_cast<char**>(kwlist), &soft, &back)) {
|
||||
static const char* kwlist[] = {"confirm", "quit_type", nullptr};
|
||||
QuitType quit_type = QuitType::kSoft;
|
||||
PyObject* quit_type_obj{Py_None};
|
||||
int confirm{};
|
||||
if (!PyArg_ParseTupleAndKeywords(args, keywds, "|pO",
|
||||
const_cast<char**>(kwlist), &confirm,
|
||||
&quit_type_obj)) {
|
||||
return nullptr;
|
||||
}
|
||||
QuitType quit_type{};
|
||||
if (back) {
|
||||
if (!soft) {
|
||||
Log(LogLevel::kWarning,
|
||||
"Got soft=False back=True in quit() which is ambiguous.");
|
||||
}
|
||||
quit_type = QuitType::kBack;
|
||||
} else if (soft) {
|
||||
quit_type = QuitType::kSoft;
|
||||
} else {
|
||||
quit_type = QuitType::kHard;
|
||||
if (quit_type_obj != Py_None) {
|
||||
quit_type = BasePython::GetPyEnum_QuitType(quit_type_obj);
|
||||
}
|
||||
g_base->QuitApp(quit_type);
|
||||
|
||||
g_base->QuitApp(confirm, quit_type);
|
||||
Py_RETURN_NONE;
|
||||
BA_PYTHON_CATCH;
|
||||
}
|
||||
@ -540,18 +532,17 @@ static PyMethodDef PyQuitDef = {
|
||||
(PyCFunction)PyQuit, // method
|
||||
METH_VARARGS | METH_KEYWORDS, // flags
|
||||
|
||||
"quit(soft: bool = True, back: bool = False) -> None\n"
|
||||
"quit(confirm: bool = False,\n"
|
||||
" quit_type: babase.QuitType = babase.QuitType.SOFT\n"
|
||||
") -> None\n"
|
||||
"\n"
|
||||
"Quit the app.\n"
|
||||
"\n"
|
||||
"Category: **General Utility Functions**\n"
|
||||
"\n"
|
||||
"On platforms such as mobile, a 'soft' quit may background and/or reset\n"
|
||||
"the app but keep it running. A 'back' quit is a special form of soft\n"
|
||||
"quit that may trigger different behavior in the OS. On Android, for\n"
|
||||
"example, a back-quit may simply jump to the previous Android activity,\n"
|
||||
"while a regular soft quit may just exit the current activity and dump\n"
|
||||
"the user at their home screen."};
|
||||
"If 'confirm' is True, a confirm dialog will be presented if conditions\n"
|
||||
"allow; otherwise the quit will still be immediate.\n"
|
||||
"See docs for babase.QuitType for explanations of its behavior."};
|
||||
|
||||
// ----------------------------- apply_config ----------------------------------
|
||||
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "ballistica/base/assets/assets.h"
|
||||
#include "ballistica/base/assets/sound_asset.h"
|
||||
#include "ballistica/base/input/input.h"
|
||||
#include "ballistica/base/platform/base_platform.h"
|
||||
|
||||
@ -26,7 +26,7 @@ void StdioConsole::StartInMainThread() {
|
||||
|
||||
// Spin up our thread.
|
||||
event_loop_ = new EventLoop(EventLoopID::kStdin);
|
||||
g_core->pausable_event_loops.push_back(event_loop_);
|
||||
g_core->suspendable_event_loops.push_back(event_loop_);
|
||||
|
||||
// Tell our thread to start reading.
|
||||
event_loop()->PushCall([this] {
|
||||
|
||||
@ -11,12 +11,8 @@
|
||||
#include "ballistica/base/logic/logic.h"
|
||||
#include "ballistica/base/platform/base_platform.h"
|
||||
#include "ballistica/base/python/base_python.h"
|
||||
#include "ballistica/base/support/context.h"
|
||||
#include "ballistica/base/ui/ui.h"
|
||||
#include "ballistica/core/core.h"
|
||||
#include "ballistica/core/platform/support/min_sdl.h"
|
||||
#include "ballistica/shared/foundation/event_loop.h"
|
||||
#include "ballistica/shared/foundation/macros.h"
|
||||
#include "ballistica/shared/generic/utils.h"
|
||||
#include "ballistica/shared/python/python_command.h"
|
||||
#include "ballistica/shared/python/python_sys.h"
|
||||
@ -102,20 +98,6 @@ static void DrawRect(RenderPass* pass, Mesh* mesh, float bottom, float x,
|
||||
c.Translate(x, y + bottom, kDevConsoleZDepth);
|
||||
c.DrawMesh(mesh);
|
||||
}
|
||||
// Draw text.
|
||||
// {
|
||||
// auto xf = c.ScopedTransform();
|
||||
// c.Translate(x + width * 0.5f, y + bottom + height * 0.5f,
|
||||
// kDevConsoleZDepth);
|
||||
// c.Scale(tscale, tscale, 1.0f);
|
||||
// int elem_count = tgrp->GetElementCount();
|
||||
// c.SetColor(fgcolor.x, fgcolor.y, fgcolor.z, 1.0f);
|
||||
// c.SetFlatness(1.0f);
|
||||
// for (int e = 0; e < elem_count; e++) {
|
||||
// c.SetTexture(tgrp->GetElementTexture(e));
|
||||
// c.DrawMesh(tgrp->GetElementMesh(e));
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
static void DrawText(RenderPass* pass, TextGroup* tgrp, float tscale,
|
||||
|
||||
@ -7,12 +7,10 @@
|
||||
#include "ballistica/base/input/device/keyboard_input.h"
|
||||
#include "ballistica/base/input/input.h"
|
||||
#include "ballistica/base/logic/logic.h"
|
||||
#include "ballistica/base/python/base_python.h"
|
||||
#include "ballistica/base/support/app_config.h"
|
||||
#include "ballistica/base/support/ui_v1_soft.h"
|
||||
#include "ballistica/base/ui/dev_console.h"
|
||||
#include "ballistica/base/ui/ui_delegate.h"
|
||||
#include "ballistica/shared/foundation/event_loop.h"
|
||||
#include "ballistica/shared/foundation/inline.h"
|
||||
#include "ballistica/shared/generic/utils.h"
|
||||
|
||||
namespace ballistica::base {
|
||||
@ -59,9 +57,11 @@ void UI::StepDisplayTime() {
|
||||
void UI::OnAppStart() {
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
if (g_base->HaveUIV1()) {
|
||||
g_base->ui_v1()->OnAppStart();
|
||||
}
|
||||
// if (auto* ui_delegate = g_base->ui->delegate()) {
|
||||
// printf("HAVE DEL %d\n",
|
||||
// static_cast<int>(g_base->ui->delegate() != nullptr));
|
||||
// g_base->ui_v1()->OnAppStart();
|
||||
// }
|
||||
|
||||
// Make sure user knows when forced-ui-scale is enabled.
|
||||
if (force_scale_) {
|
||||
@ -92,36 +92,36 @@ void UI::OnAppShutdownComplete() { assert(g_base->InLogicThread()); }
|
||||
|
||||
void UI::DoApplyAppConfig() {
|
||||
assert(g_base->InLogicThread());
|
||||
if (g_base->HaveUIV1()) {
|
||||
g_base->ui_v1()->DoApplyAppConfig();
|
||||
if (auto* ui_delegate = g_base->ui->delegate()) {
|
||||
ui_delegate->DoApplyAppConfig();
|
||||
}
|
||||
show_dev_console_button_ =
|
||||
g_base->app_config->Resolve(AppConfig::BoolID::kShowDevConsoleButton);
|
||||
}
|
||||
|
||||
auto UI::MainMenuVisible() const -> bool {
|
||||
if (g_base->HaveUIV1()) {
|
||||
return g_base->ui_v1()->MainMenuVisible();
|
||||
if (auto* ui_delegate = g_base->ui->delegate()) {
|
||||
return ui_delegate->MainMenuVisible();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto UI::PartyIconVisible() -> bool {
|
||||
if (g_base->HaveUIV1()) {
|
||||
return g_base->ui_v1()->PartyIconVisible();
|
||||
if (auto* ui_delegate = g_base->ui->delegate()) {
|
||||
return ui_delegate->PartyIconVisible();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void UI::ActivatePartyIcon() {
|
||||
if (g_base->HaveUIV1()) {
|
||||
g_base->ui_v1()->ActivatePartyIcon();
|
||||
if (auto* ui_delegate = g_base->ui->delegate()) {
|
||||
ui_delegate->ActivatePartyIcon();
|
||||
}
|
||||
}
|
||||
|
||||
auto UI::PartyWindowOpen() -> bool {
|
||||
if (g_base->HaveUIV1()) {
|
||||
return g_base->ui_v1()->PartyWindowOpen();
|
||||
if (auto* ui_delegate = g_base->ui->delegate()) {
|
||||
return ui_delegate->PartyWindowOpen();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -145,8 +145,10 @@ auto UI::HandleMouseDown(int button, float x, float y, bool double_click)
|
||||
handled = dev_console_->HandleMouseDown(button, x, y);
|
||||
}
|
||||
|
||||
if (!handled && g_base->HaveUIV1()) {
|
||||
handled = g_base->ui_v1()->HandleLegacyRootUIMouseDown(x, y);
|
||||
if (!handled) {
|
||||
if (auto* ui_delegate = g_base->ui->delegate()) {
|
||||
handled = ui_delegate->HandleLegacyRootUIMouseDown(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
@ -176,8 +178,8 @@ void UI::HandleMouseUp(int button, float x, float y) {
|
||||
}
|
||||
}
|
||||
|
||||
if (g_base->HaveUIV1()) {
|
||||
g_base->ui_v1()->HandleLegacyRootUIMouseUp(x, y);
|
||||
if (auto* ui_delegate = g_base->ui->delegate()) {
|
||||
ui_delegate->HandleLegacyRootUIMouseUp(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
@ -205,8 +207,8 @@ void UI::HandleMouseMotion(float x, float y) {
|
||||
SendWidgetMessage(
|
||||
WidgetMessage(WidgetMessage::Type::kMouseMove, nullptr, x, y));
|
||||
|
||||
if (g_base->HaveUIV1()) {
|
||||
g_base->ui_v1()->HandleLegacyRootUIMouseMotion(x, y);
|
||||
if (auto* ui_delegate = g_base->ui->delegate()) {
|
||||
ui_delegate->HandleLegacyRootUIMouseMotion(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,8 +234,8 @@ void UI::PushMainMenuPressCall(InputDevice* device) {
|
||||
|
||||
void UI::MainMenuPress_(InputDevice* device) {
|
||||
assert(g_base->InLogicThread());
|
||||
if (g_base->HaveUIV1()) {
|
||||
g_base->ui_v1()->DoHandleDeviceMenuPress(device);
|
||||
if (auto* ui_delegate = g_base->ui->delegate()) {
|
||||
ui_delegate->DoHandleDeviceMenuPress(device);
|
||||
} else {
|
||||
Log(LogLevel::kWarning,
|
||||
"UI::MainMenuPress called without ui_v1 present; unexpected.");
|
||||
@ -250,8 +252,8 @@ void UI::SetUIInputDevice(InputDevice* input_device) {
|
||||
}
|
||||
|
||||
void UI::Reset() {
|
||||
if (g_base->HaveUIV1()) {
|
||||
g_base->ui_v1()->Reset();
|
||||
if (auto* ui_delegate = g_base->ui->delegate()) {
|
||||
ui_delegate->Reset();
|
||||
}
|
||||
}
|
||||
|
||||
@ -267,21 +269,21 @@ auto UI::ShouldShowButtonShortcuts() const -> bool {
|
||||
}
|
||||
|
||||
auto UI::SendWidgetMessage(const WidgetMessage& m) -> int {
|
||||
if (g_base->HaveUIV1()) {
|
||||
return g_base->ui_v1()->SendWidgetMessage(m);
|
||||
if (auto* ui_delegate = g_base->ui->delegate()) {
|
||||
return ui_delegate->SendWidgetMessage(m);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void UI::OnScreenSizeChange() {
|
||||
if (g_base->HaveUIV1()) {
|
||||
g_base->ui_v1()->OnScreenSizeChange();
|
||||
if (auto* ui_delegate = g_base->ui->delegate()) {
|
||||
ui_delegate->OnScreenSizeChange();
|
||||
}
|
||||
}
|
||||
|
||||
void UI::LanguageChanged() {
|
||||
if (g_base->HaveUIV1()) {
|
||||
g_base->ui_v1()->OnLanguageChange();
|
||||
if (auto* ui_delegate = g_base->ui->delegate()) {
|
||||
ui_delegate->OnLanguageChange();
|
||||
}
|
||||
}
|
||||
|
||||
@ -313,7 +315,9 @@ auto UI::GetWidgetForInput(InputDevice* input_device) -> ui_v1::Widget* {
|
||||
// events are received by that device for a long time, it is up for grabs
|
||||
// to the next device that requests it.
|
||||
|
||||
if (!g_base->HaveUIV1()) {
|
||||
auto* ui_delegate = g_base->ui->delegate();
|
||||
|
||||
if (!ui_delegate) {
|
||||
ret_val = nullptr;
|
||||
} else if ((GetUIInputDevice() == nullptr)
|
||||
|| (input_device == GetUIInputDevice())
|
||||
@ -325,7 +329,7 @@ auto UI::GetWidgetForInput(InputDevice* input_device) -> ui_v1::Widget* {
|
||||
// seconds ago to automatically own a newly created widget).
|
||||
last_input_device_use_time_ = time;
|
||||
ui_input_device_ = input_device;
|
||||
ret_val = g_base->ui_v1()->GetRootWidget();
|
||||
ret_val = ui_delegate->GetRootWidget();
|
||||
} else {
|
||||
// For rejected input devices, play error sounds sometimes so they know
|
||||
// they're not the chosen one.
|
||||
@ -382,8 +386,8 @@ auto UI::GetWidgetForInput(InputDevice* input_device) -> ui_v1::Widget* {
|
||||
}
|
||||
|
||||
void UI::Draw(FrameDef* frame_def) {
|
||||
if (g_base->HaveUIV1()) {
|
||||
g_base->ui_v1()->Draw(frame_def);
|
||||
if (auto* ui_delegate = g_base->ui->delegate()) {
|
||||
ui_delegate->Draw(frame_def);
|
||||
}
|
||||
}
|
||||
|
||||
@ -468,40 +472,43 @@ void UI::DrawDevConsoleButton_(FrameDef* frame_def) {
|
||||
}
|
||||
|
||||
void UI::ShowURL(const std::string& url) {
|
||||
if (g_base->HaveUIV1()) {
|
||||
g_base->ui_v1()->DoShowURL(url);
|
||||
if (auto* ui_delegate = g_base->ui->delegate()) {
|
||||
ui_delegate->DoShowURL(url);
|
||||
} else {
|
||||
Log(LogLevel::kWarning,
|
||||
"UI::ShowURL called without g_ui_v1_soft present; unexpected.");
|
||||
Log(LogLevel::kWarning, "UI::ShowURL called without ui_delegate present.");
|
||||
}
|
||||
}
|
||||
|
||||
void UI::ConfirmQuit() {
|
||||
g_base->logic->event_loop()->PushCall([this] {
|
||||
// If the in-app console is active, dismiss it.
|
||||
if (dev_console_ != nullptr && dev_console_->IsActive()) {
|
||||
dev_console_->Dismiss();
|
||||
}
|
||||
void UI::set_ui_delegate(base::UIDelegateInterface* delegate) {
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
assert(g_base->InLogicThread());
|
||||
// If we're headless 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->QuitApp();
|
||||
return;
|
||||
} else {
|
||||
ScopedSetContext ssc(nullptr);
|
||||
g_base->audio->PlaySound(g_base->assets->SysSound(SysSoundID::kSwish));
|
||||
g_base->ui_v1()->DoQuitWindow();
|
||||
if (delegate == delegate_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have a keyboard, give it UI ownership.
|
||||
InputDevice* keyboard = g_base->input->keyboard_input();
|
||||
if (keyboard) {
|
||||
g_base->ui->SetUIInputDevice(keyboard);
|
||||
}
|
||||
try {
|
||||
auto* old_delegate = delegate_;
|
||||
delegate_ = nullptr;
|
||||
if (old_delegate) {
|
||||
old_delegate->OnDeactivate();
|
||||
}
|
||||
});
|
||||
delegate_ = delegate;
|
||||
if (delegate_) {
|
||||
delegate_->OnActivate();
|
||||
|
||||
// Inform them that a few things changed, since they might have since
|
||||
// the last time they were active (these callbacks only go to the *active*
|
||||
// ui delegate).
|
||||
delegate_->DoApplyAppConfig();
|
||||
delegate_->OnScreenSizeChange();
|
||||
delegate_->OnLanguageChange();
|
||||
}
|
||||
} catch (const Exception& exc) {
|
||||
// Switching UI delegates is a big deal; don't try to continue if
|
||||
// something goes wrong.
|
||||
FatalError(std::string("Error setting native layer ui-delegate: ")
|
||||
+ exc.what());
|
||||
}
|
||||
}
|
||||
|
||||
void UI::PushDevConsolePrintCall(const std::string& msg) {
|
||||
|
||||
@ -40,6 +40,8 @@ class UI {
|
||||
/// switching app-modes or when resetting things within an app mode.
|
||||
void Reset();
|
||||
|
||||
void set_ui_delegate(base::UIDelegateInterface* delegate);
|
||||
|
||||
/// Pop up an in-app window to display a URL (NOT to open the URL in a
|
||||
/// browser). Can be called from any thread.
|
||||
void ShowURL(const std::string& url);
|
||||
@ -47,7 +49,7 @@ class UI {
|
||||
/// High level call to request a quit; ideally with a confirmation ui.
|
||||
/// When a UI can't be shown, triggers an immediate shutdown. This can be
|
||||
/// called from any thread.
|
||||
void ConfirmQuit();
|
||||
// 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.
|
||||
@ -111,12 +113,15 @@ class UI {
|
||||
|
||||
void PushDevConsolePrintCall(const std::string& msg);
|
||||
|
||||
auto* delegate() const { return delegate_; }
|
||||
|
||||
private:
|
||||
void MainMenuPress_(InputDevice* device);
|
||||
auto DevConsoleButtonSize_() const -> float;
|
||||
auto InDevConsoleButton_(float x, float y) const -> bool;
|
||||
void DrawDevConsoleButton_(FrameDef* frame_def);
|
||||
|
||||
base::UIDelegateInterface* delegate_{};
|
||||
DevConsole* dev_console_{};
|
||||
std::string dev_console_startup_messages_;
|
||||
Object::WeakRef<InputDevice> ui_input_device_;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// Released under the MIT License. See LICENSE for details.
|
||||
|
||||
#ifndef BALLISTICA_BASE_SUPPORT_UI_V1_SOFT_H_
|
||||
#define BALLISTICA_BASE_SUPPORT_UI_V1_SOFT_H_
|
||||
#ifndef BALLISTICA_BASE_UI_UI_DELEGATE_H_
|
||||
#define BALLISTICA_BASE_UI_UI_DELEGATE_H_
|
||||
|
||||
#include "ballistica/base/ui/ui.h"
|
||||
|
||||
@ -14,15 +14,21 @@ class Widget;
|
||||
|
||||
namespace ballistica::base {
|
||||
|
||||
/// 'Soft' interface to the ui_v1 feature-set, managed by base. Feature-sets
|
||||
/// listing ui_v1 as a soft requirement must limit their use of it to these
|
||||
/// methods and should be prepared to handle the not-present case.
|
||||
class UIV1SoftInterface {
|
||||
class UIDelegateInterface {
|
||||
public:
|
||||
/// Called when this delegate is becoming the active one.
|
||||
virtual void OnActivate() = 0;
|
||||
|
||||
/// Called when this delegate is resigning active status.
|
||||
virtual void OnDeactivate() = 0;
|
||||
|
||||
virtual void OnScreenSizeChange() = 0;
|
||||
virtual void OnLanguageChange() = 0;
|
||||
virtual void DoApplyAppConfig() = 0;
|
||||
|
||||
virtual void DoHandleDeviceMenuPress(base::InputDevice* device) = 0;
|
||||
virtual void DoShowURL(const std::string& url) = 0;
|
||||
virtual void DoQuitWindow() = 0;
|
||||
virtual auto NewRootUI() -> ui_v1::RootUI* = 0;
|
||||
// virtual void DoQuitWindow() = 0;
|
||||
virtual auto MainMenuVisible() -> bool = 0;
|
||||
virtual auto PartyIconVisible() -> bool = 0;
|
||||
virtual void ActivatePartyIcon() = 0;
|
||||
@ -30,16 +36,20 @@ class UIV1SoftInterface {
|
||||
virtual auto HandleLegacyRootUIMouseDown(float x, float y) -> bool = 0;
|
||||
virtual void HandleLegacyRootUIMouseUp(float x, float y) = 0;
|
||||
virtual void Draw(FrameDef* frame_def) = 0;
|
||||
virtual void OnAppStart() = 0;
|
||||
virtual auto PartyWindowOpen() -> bool = 0;
|
||||
virtual void Reset() = 0;
|
||||
virtual void OnScreenSizeChange() = 0;
|
||||
virtual void OnLanguageChange() = 0;
|
||||
virtual auto GetRootWidget() -> ui_v1::Widget* = 0;
|
||||
virtual auto SendWidgetMessage(const WidgetMessage& m) -> int = 0;
|
||||
virtual void DoApplyAppConfig() = 0;
|
||||
|
||||
/// Should return true if this app mode can confirm quitting the app.
|
||||
virtual auto HasQuitConfirmDialog() -> bool = 0;
|
||||
|
||||
/// Will be called in the logic thread if HasQuitConfirmDialog() returns
|
||||
/// true. Should present a quit confirmation dialog to the user and call
|
||||
/// BaseFeatureSet::QuitApp() with the provided quit_type if confirmed.
|
||||
virtual void ConfirmQuit(QuitType quit_type) = 0;
|
||||
};
|
||||
|
||||
} // namespace ballistica::base
|
||||
|
||||
#endif // BALLISTICA_BASE_SUPPORT_UI_V1_SOFT_H_
|
||||
#endif // BALLISTICA_BASE_UI_UI_DELEGATE_H_
|
||||
@ -157,10 +157,10 @@ class CoreFeatureSet {
|
||||
|
||||
// The following are misc values that should be migrated to applicable
|
||||
// subsystem classes.
|
||||
bool threads_paused{};
|
||||
bool event_loops_suspended{};
|
||||
bool workspaces_in_use{};
|
||||
bool replay_open{};
|
||||
std::vector<EventLoop*> pausable_event_loops;
|
||||
std::vector<EventLoop*> suspendable_event_loops;
|
||||
std::mutex v1_cloud_log_mutex;
|
||||
std::string v1_cloud_log;
|
||||
bool did_put_v1_cloud_log{};
|
||||
@ -168,7 +168,7 @@ class CoreFeatureSet {
|
||||
int master_server_source{};
|
||||
int session_count{};
|
||||
bool have_incentivized_ad{false};
|
||||
bool should_pause{};
|
||||
bool should_pause_active_game{};
|
||||
bool reset_vr_orientation{};
|
||||
bool user_ran_commands{};
|
||||
std::thread::id main_thread_id{};
|
||||
|
||||
@ -21,6 +21,7 @@ class BaseSoftInterface {
|
||||
virtual auto InAssetsThread() const -> bool = 0;
|
||||
virtual auto InLogicThread() const -> bool = 0;
|
||||
virtual auto InAudioThread() const -> bool = 0;
|
||||
virtual auto InGraphicsContext() const -> bool = 0;
|
||||
virtual auto InBGDynamicsThread() const -> bool = 0;
|
||||
virtual auto InNetworkWriteThread() const -> bool = 0;
|
||||
virtual void PlusDirectSendV1CloudLogs(const std::string& prefix,
|
||||
|
||||
@ -28,20 +28,26 @@ class Node : public Object {
|
||||
public:
|
||||
Node(Scene* scene, NodeType* node_type);
|
||||
~Node() override;
|
||||
auto id() const -> int64_t {
|
||||
return id_;
|
||||
} // Return the node's id in its scene.
|
||||
virtual void Step() {} // Called for each step of the sim.
|
||||
virtual void OnScreenSizeChange() {} // Called when screen size changes.
|
||||
virtual void OnLanguageChange() {} // Called when the language changes.
|
||||
|
||||
/// Return the node's id in its scene.
|
||||
auto id() const -> int64_t { return id_; }
|
||||
|
||||
/// Called for each step of the sim.
|
||||
virtual void Step() {}
|
||||
|
||||
/// Called when screen size changes.
|
||||
virtual void OnScreenSizeChange() {}
|
||||
|
||||
/// Called when the language changes.
|
||||
virtual void OnLanguageChange() {}
|
||||
virtual void OnGraphicsQualityChanged(base::GraphicsQuality q) {}
|
||||
|
||||
// The node can rule out collisions between particular bodies using this.
|
||||
/// The node can rule out collisions between particular bodies using this.
|
||||
virtual auto PreFilterCollision(RigidBody* b1, RigidBody* r2) -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pull a node type out of a buffer.
|
||||
/// Pull a node type out of a buffer.
|
||||
static auto extract_node_message_type(const char** b) -> NodeMessageType {
|
||||
auto t = static_cast<NodeMessageType>(**b);
|
||||
(*b) += 1;
|
||||
@ -51,10 +57,10 @@ class Node : public Object {
|
||||
void ConnectAttribute(NodeAttributeUnbound* src_attr, Node* dst_node,
|
||||
NodeAttributeUnbound* dst_attr);
|
||||
|
||||
// Return an attribute by name.
|
||||
/// Return an attribute by name.
|
||||
auto GetAttribute(const std::string& name) -> NodeAttribute;
|
||||
|
||||
// Return an attribute by index.
|
||||
/// Return an attribute by index.
|
||||
auto GetAttribute(int index) -> NodeAttribute;
|
||||
|
||||
void SetDelegate(PyObject* delegate_obj);
|
||||
@ -62,37 +68,35 @@ class Node : public Object {
|
||||
auto NewPyRef() -> PyObject* { return GetPyRef(true); }
|
||||
auto BorrowPyRef() -> PyObject* { return GetPyRef(false); }
|
||||
|
||||
// Return the delegate, or nullptr if it doesn't have one
|
||||
// (or if the delegate has since died).
|
||||
/// Return the delegate, or nullptr if it doesn't have one (or if the
|
||||
/// delegate has since died).
|
||||
auto GetDelegate() -> PyObject*;
|
||||
|
||||
void AddNodeDeathAction(PyObject* call_obj);
|
||||
|
||||
// Add a node to auto-kill when this one dies.
|
||||
/// Add a node to auto-kill when this one dies.
|
||||
void AddDependentNode(Node* node);
|
||||
|
||||
// Update birth times for all the node's parts.
|
||||
// This should be done when teleporting or otherwise spawning at
|
||||
// a new location.
|
||||
/// Update birth times for all the node's parts. This should be done when
|
||||
/// teleporting or otherwise spawning at a new location.
|
||||
void UpdatePartBirthTimes();
|
||||
|
||||
// Retrieve an existing part from a node.
|
||||
/// Retrieve an existing part from a node.
|
||||
auto GetPart(unsigned int id) -> Part* {
|
||||
assert(id < parts_.size());
|
||||
return parts_[id];
|
||||
}
|
||||
|
||||
// Used by RigidBodies when adding themselves to the part.
|
||||
/// Used by RigidBodies when adding themselves to the part.
|
||||
auto AddPart(Part* part_in) -> int {
|
||||
parts_.push_back(part_in);
|
||||
return static_cast<int>(parts_.size() - 1);
|
||||
}
|
||||
|
||||
// Used to send messages to a node
|
||||
/// Used to send messages to a node
|
||||
void DispatchNodeMessage(const char* buffer);
|
||||
|
||||
// Used to send custom user messages to a node
|
||||
// returns true if handled.
|
||||
/// Used to send custom user messages to a node.
|
||||
void DispatchUserMessage(PyObject* obj, const char* label);
|
||||
void DispatchOutOfBoundsMessage();
|
||||
void DispatchPickedUpMessage(Node* n);
|
||||
@ -102,21 +106,21 @@ class Node : public Object {
|
||||
void DispatchShouldShatterMessage();
|
||||
void DispatchImpactDamageMessage(float intensity);
|
||||
|
||||
// Utility function to get a rigid body.
|
||||
/// Utility function to get a rigid body.
|
||||
virtual auto GetRigidBody(int id) -> RigidBody* { return nullptr; }
|
||||
|
||||
// Given a rigid body, return the relative position where it should be picked
|
||||
// up from.
|
||||
/// Given a rigid body, return the relative position where it should be
|
||||
/// picked up from.
|
||||
virtual void GetRigidBodyPickupLocations(int id, float* posObj,
|
||||
float* pos_char,
|
||||
float* hand_offset_1,
|
||||
float* hand_offset_2);
|
||||
|
||||
// Called for each Node when it should render itself.
|
||||
/// Called for each Node when it should render itself.
|
||||
virtual void Draw(base::FrameDef* frame_def);
|
||||
|
||||
// Called for each node once construction is completed
|
||||
// this can be a good time to create things from the initial attr set, etc
|
||||
/// Called for each node once construction is completed this can be a good
|
||||
/// time to create things from the initial attr set, etc
|
||||
virtual void OnCreate();
|
||||
|
||||
auto scene() const -> Scene* {
|
||||
@ -124,14 +128,14 @@ class Node : public Object {
|
||||
return scene_;
|
||||
}
|
||||
|
||||
// Used to re-sync client versions of a node from the host version.
|
||||
/// Used to re-sync client versions of a node from the host version.
|
||||
virtual auto GetResyncDataSize() -> int;
|
||||
virtual auto GetResyncData() -> std::vector<uint8_t>;
|
||||
virtual void ApplyResyncData(const std::vector<uint8_t>& data);
|
||||
auto context_ref() const -> const ContextRefSceneV1& { return context_ref_; }
|
||||
|
||||
// Node labels are purely for local debugging - they aren't unique or sent
|
||||
// across the network or anything.
|
||||
/// Node labels are purely for local debugging - they aren't unique or
|
||||
/// sent across the network or anything.
|
||||
void set_label(const std::string& label) { label_ = label; }
|
||||
auto label() const -> const std::string& { return label_; }
|
||||
|
||||
@ -156,17 +160,21 @@ class Node : public Object {
|
||||
auto GetObjectDescription() const -> std::string override;
|
||||
|
||||
auto parts() const -> const std::vector<Part*>& { return parts_; }
|
||||
|
||||
auto death_actions() const
|
||||
-> const std::vector<Object::Ref<base::PythonContextCall> >& {
|
||||
return death_actions_;
|
||||
}
|
||||
|
||||
auto dependent_nodes() const -> const std::vector<Object::WeakRef<Node> >& {
|
||||
return dependent_nodes_;
|
||||
}
|
||||
|
||||
auto attribute_connections() const
|
||||
-> const std::list<Object::Ref<NodeAttributeConnection> >& {
|
||||
return attribute_connections_;
|
||||
}
|
||||
|
||||
auto attribute_connections_incoming() const
|
||||
-> const std::unordered_map<int, Object::Ref<NodeAttributeConnection> >& {
|
||||
return attribute_connections_incoming_;
|
||||
@ -177,13 +185,14 @@ class Node : public Object {
|
||||
assert(stream_id_ == -1);
|
||||
stream_id_ = val;
|
||||
}
|
||||
|
||||
void clear_stream_id() {
|
||||
assert(stream_id_ != -1);
|
||||
stream_id_ = -1;
|
||||
}
|
||||
|
||||
// Return a reference to a python wrapper for this node,
|
||||
// creating one if need be.
|
||||
/// Return a reference to a python wrapper for this node, creating one if
|
||||
/// need be.
|
||||
auto GetPyRef(bool new_ref = true) -> PyObject*;
|
||||
|
||||
void AddToScene(Scene* scene);
|
||||
@ -197,8 +206,8 @@ class Node : public Object {
|
||||
|
||||
PyObject* py_ref_ = nullptr;
|
||||
|
||||
// FIXME - We can get by with *just* a pointer to our scene
|
||||
// if we add a way to pull context from a scene.
|
||||
/// FIXME - We can get by with *just* a pointer to our scene if we add a
|
||||
/// way to pull context from a scene.
|
||||
ContextRefSceneV1 context_ref_;
|
||||
Scene* scene_{};
|
||||
std::string label_;
|
||||
@ -211,10 +220,10 @@ class Node : public Object {
|
||||
PythonRef delegate_;
|
||||
std::vector<Object::Ref<base::PythonContextCall> > death_actions_;
|
||||
|
||||
// Outgoing attr connections in order created.
|
||||
/// Outgoing attr connections in order created.
|
||||
std::list<Object::Ref<NodeAttributeConnection> > attribute_connections_;
|
||||
|
||||
// Incoming attr connections by attr index.
|
||||
/// Incoming attr connections by attr index.
|
||||
std::unordered_map<int, Object::Ref<NodeAttributeConnection> >
|
||||
attribute_connections_incoming_;
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
#include "ballistica/shared/foundation/event_loop.h"
|
||||
#include "ballistica/shared/generic/json.h"
|
||||
#include "ballistica/shared/generic/utils.h"
|
||||
#include "ballistica/ui_v1/ui_v1.h"
|
||||
|
||||
namespace ballistica::scene_v1 {
|
||||
|
||||
@ -76,8 +77,14 @@ bool SceneV1AppMode::InClassicMainMenuSession() const {
|
||||
static SceneV1AppMode* g_scene_v1_app_mode{};
|
||||
|
||||
void SceneV1AppMode::OnActivate() {
|
||||
assert(g_base->InLogicThread());
|
||||
Reset();
|
||||
|
||||
// We use UIV1.
|
||||
if (!g_core->HeadlessMode()) {
|
||||
g_base->ui->set_ui_delegate(ui_v1::UIV1FeatureSet::Import());
|
||||
}
|
||||
|
||||
// To set initial states, explicitly fire some of our 'On-Foo-Changed'
|
||||
// callbacks.
|
||||
DoApplyAppConfig();
|
||||
|
||||
@ -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 = 21422;
|
||||
const int kEngineBuildNumber = 21443;
|
||||
const char* kEngineVersion = "1.7.28";
|
||||
const int kEngineApiVersion = 8;
|
||||
|
||||
|
||||
@ -140,10 +140,6 @@ namespace ballistica {
|
||||
#define BA_ENABLE_STDIO_CONSOLE 0
|
||||
#endif
|
||||
|
||||
#ifndef BA_HARDWARE_CURSOR
|
||||
#define BA_HARDWARE_CURSOR 0
|
||||
#endif
|
||||
|
||||
#ifndef BA_ENABLE_OS_FONT_RENDERING
|
||||
#define BA_ENABLE_OS_FONT_RENDERING 0
|
||||
#endif
|
||||
@ -277,7 +273,6 @@ class BuildConfig {
|
||||
bool enable_os_font_rendering() const {
|
||||
return EXPBOOL_(BA_ENABLE_OS_FONT_RENDERING);
|
||||
}
|
||||
bool hardware_cursor() const { return EXPBOOL_(BA_HARDWARE_CURSOR); }
|
||||
};
|
||||
|
||||
#undef EXPBOOL_
|
||||
|
||||
@ -171,12 +171,13 @@ auto EventLoop::ThreadMainAssetsP_(void* data) -> void* {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void EventLoop::PushSetPaused(bool paused) {
|
||||
void EventLoop::PushSetSuspended(bool suspended) {
|
||||
assert(g_core);
|
||||
// Can be toggled from the main thread only.
|
||||
assert(std::this_thread::get_id() == g_core->main_thread_id);
|
||||
PushThreadMessage_(ThreadMessage_(paused ? ThreadMessage_::Type::kPause
|
||||
: ThreadMessage_::Type::kResume));
|
||||
PushThreadMessage_(ThreadMessage_(suspended
|
||||
? ThreadMessage_::Type::kSuspend
|
||||
: ThreadMessage_::Type::kUnsuspend));
|
||||
}
|
||||
|
||||
void EventLoop::WaitForNextEvent_(bool single_cycle) {
|
||||
@ -184,24 +185,27 @@ void EventLoop::WaitForNextEvent_(bool single_cycle) {
|
||||
|
||||
// If we're running a single cycle we never stop to wait.
|
||||
if (single_cycle) {
|
||||
// Need to revisit this if we ever do single-cycle for
|
||||
// the gil-holding thread so we don't starve other Python threads.
|
||||
// Need to revisit this if we ever do single-cycle for the gil-holding
|
||||
// thread so we don't starve other Python threads.
|
||||
assert(!acquires_python_gil_);
|
||||
return;
|
||||
}
|
||||
|
||||
// We also never wait if we have pending runnables; we want to run
|
||||
// things as soon as we can. We chew through all runnables at the end
|
||||
// of the loop so it might seem like there should never be any here,
|
||||
// but runnables can add other runnables that won't get processed until
|
||||
// the next time through.
|
||||
// BUG FIX: We now skip this if we're paused since we don't run runnables
|
||||
// in that case. This was preventing us from releasing the GIL while paused
|
||||
// (and I assume causing us to spin full-speed through the loop; ugh).
|
||||
// NOTE: It is theoretically possible for a runnable to add another runnable
|
||||
// each time through the loop which would effectively starve the GIL as
|
||||
// well; do we need to worry about that case?
|
||||
if (has_pending_runnables() && !paused_) {
|
||||
// We also never wait if we have pending runnables; we want to run things
|
||||
// as soon as we can. We chew through all runnables at the end of the loop
|
||||
// so it might seem like there should never be any here, but runnables can
|
||||
// add other runnables that won't get processed until the next time
|
||||
// through.
|
||||
//
|
||||
// BUG FIX: We now skip this if we're suspended since we don't run
|
||||
// runnables in that case. This was preventing us from releasing the GIL
|
||||
// while suspended (and I assume causing us to spin full-speed through
|
||||
// the loop; ugh).
|
||||
//
|
||||
// NOTE: It is theoretically possible for a runnable to add another
|
||||
// runnable each time through the loop which would effectively starve the
|
||||
// GIL as well; do we need to worry about that case?
|
||||
if (has_pending_runnables() && !suspended_) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -212,7 +216,7 @@ void EventLoop::WaitForNextEvent_(bool single_cycle) {
|
||||
|
||||
// If we've got active timers, wait for messages with a timeout so we can
|
||||
// run the next timer payload.
|
||||
if (!paused_ && timers_.ActiveTimerCount() > 0) {
|
||||
if (!suspended_ && timers_.ActiveTimerCount() > 0) {
|
||||
millisecs_t apptime = g_core->GetAppTimeMillisecs();
|
||||
millisecs_t wait_time = timers_.TimeToNextExpire(apptime);
|
||||
if (wait_time > 0) {
|
||||
@ -287,18 +291,18 @@ void EventLoop::Run_(bool single_cycle) {
|
||||
done_ = true;
|
||||
break;
|
||||
}
|
||||
case ThreadMessage_::Type::kPause: {
|
||||
assert(!paused_);
|
||||
RunPauseCallbacks_();
|
||||
paused_ = true;
|
||||
last_pause_time_ = g_core->GetAppTimeMillisecs();
|
||||
messages_since_paused_ = 0;
|
||||
case ThreadMessage_::Type::kSuspend: {
|
||||
assert(!suspended_);
|
||||
RunSuspendCallbacks_();
|
||||
suspended_ = true;
|
||||
last_suspend_time_ = g_core->GetAppTimeMillisecs();
|
||||
messages_since_suspended_ = 0;
|
||||
break;
|
||||
}
|
||||
case ThreadMessage_::Type::kResume: {
|
||||
assert(paused_);
|
||||
RunResumeCallbacks_();
|
||||
paused_ = false;
|
||||
case ThreadMessage_::Type::kUnsuspend: {
|
||||
assert(suspended_);
|
||||
RunUnsuspendCallbacks_();
|
||||
suspended_ = false;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@ -311,7 +315,7 @@ void EventLoop::Run_(bool single_cycle) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!paused_) {
|
||||
if (!suspended_) {
|
||||
timers_.Run(g_core->GetAppTimeMillisecs());
|
||||
RunPendingRunnables_();
|
||||
}
|
||||
@ -473,11 +477,11 @@ void EventLoop::LogThreadMessageTally_(
|
||||
case ThreadMessage_::Type::kRunnable:
|
||||
s += "kRunnable";
|
||||
break;
|
||||
case ThreadMessage_::Type::kPause:
|
||||
s += "kPause";
|
||||
case ThreadMessage_::Type::kSuspend:
|
||||
s += "kSuspend";
|
||||
break;
|
||||
case ThreadMessage_::Type::kResume:
|
||||
s += "kResume";
|
||||
case ThreadMessage_::Type::kUnsuspend:
|
||||
s += "kUnsuspend";
|
||||
break;
|
||||
default:
|
||||
s += "UNKNOWN(" + std::to_string(static_cast<int>(m.type)) + ")";
|
||||
@ -570,24 +574,24 @@ void EventLoop::PushThreadMessage_(const ThreadMessage_& t) {
|
||||
}
|
||||
}
|
||||
|
||||
void EventLoop::SetEventLoopsPaused(bool paused) {
|
||||
void EventLoop::SetEventLoopsSuspended(bool suspended) {
|
||||
assert(g_core);
|
||||
assert(std::this_thread::get_id() == g_core->main_thread_id);
|
||||
g_core->threads_paused = paused;
|
||||
for (auto&& i : g_core->pausable_event_loops) {
|
||||
i->PushSetPaused(paused);
|
||||
g_core->event_loops_suspended = suspended;
|
||||
for (auto&& i : g_core->suspendable_event_loops) {
|
||||
i->PushSetSuspended(suspended);
|
||||
}
|
||||
}
|
||||
|
||||
auto EventLoop::GetStillPausingThreads() -> std::vector<EventLoop*> {
|
||||
auto EventLoop::GetStillSuspendingEventLoops() -> std::vector<EventLoop*> {
|
||||
assert(g_core);
|
||||
std::vector<EventLoop*> threads;
|
||||
assert(std::this_thread::get_id() == g_core->main_thread_id);
|
||||
|
||||
// Only return results if an actual pause is in effect.
|
||||
if (g_core->threads_paused) {
|
||||
for (auto&& i : g_core->pausable_event_loops) {
|
||||
if (!i->paused()) {
|
||||
// Only return results if an actual suspend is in effect.
|
||||
if (g_core->event_loops_suspended) {
|
||||
for (auto&& i : g_core->suspendable_event_loops) {
|
||||
if (!i->suspended()) {
|
||||
threads.push_back(i);
|
||||
}
|
||||
}
|
||||
@ -595,9 +599,9 @@ auto EventLoop::GetStillPausingThreads() -> std::vector<EventLoop*> {
|
||||
return threads;
|
||||
}
|
||||
|
||||
auto EventLoop::AreEventLoopsPaused() -> bool {
|
||||
auto EventLoop::AreEventLoopsSuspended() -> bool {
|
||||
assert(g_core);
|
||||
return g_core->threads_paused;
|
||||
return g_core->event_loops_suspended;
|
||||
}
|
||||
|
||||
auto EventLoop::NewTimer(millisecs_t length, bool repeat,
|
||||
@ -678,14 +682,14 @@ void EventLoop::RunPendingRunnables_() {
|
||||
}
|
||||
}
|
||||
|
||||
void EventLoop::RunPauseCallbacks_() {
|
||||
for (Runnable* i : pause_callbacks_) {
|
||||
void EventLoop::RunSuspendCallbacks_() {
|
||||
for (Runnable* i : suspend_callbacks_) {
|
||||
i->RunAndLogErrors();
|
||||
}
|
||||
}
|
||||
|
||||
void EventLoop::RunResumeCallbacks_() {
|
||||
for (Runnable* i : resume_callbacks_) {
|
||||
void EventLoop::RunUnsuspendCallbacks_() {
|
||||
for (Runnable* i : unsuspend_callbacks_) {
|
||||
i->RunAndLogErrors();
|
||||
}
|
||||
}
|
||||
@ -701,14 +705,14 @@ void EventLoop::PushCrossThreadRunnable_(Runnable* runnable,
|
||||
EventLoop::ThreadMessage_::Type::kRunnable, runnable, completion_flag));
|
||||
}
|
||||
|
||||
void EventLoop::AddPauseCallback(Runnable* runnable) {
|
||||
void EventLoop::AddSuspendCallback(Runnable* runnable) {
|
||||
assert(std::this_thread::get_id() == thread_id());
|
||||
pause_callbacks_.push_back(runnable);
|
||||
suspend_callbacks_.push_back(runnable);
|
||||
}
|
||||
|
||||
void EventLoop::AddResumeCallback(Runnable* runnable) {
|
||||
void EventLoop::AddUnsuspendCallback(Runnable* runnable) {
|
||||
assert(std::this_thread::get_id() == thread_id());
|
||||
resume_callbacks_.push_back(runnable);
|
||||
unsuspend_callbacks_.push_back(runnable);
|
||||
}
|
||||
|
||||
void EventLoop::PushRunnable(Runnable* runnable) {
|
||||
|
||||
@ -29,8 +29,8 @@ class EventLoop {
|
||||
|
||||
static auto CurrentThreadName() -> std::string;
|
||||
|
||||
static void SetEventLoopsPaused(bool enable);
|
||||
static auto AreEventLoopsPaused() -> bool;
|
||||
static void SetEventLoopsSuspended(bool enable);
|
||||
static auto AreEventLoopsSuspended() -> bool;
|
||||
|
||||
auto ThreadIsCurrent() const -> bool {
|
||||
return std::this_thread::get_id() == thread_id();
|
||||
@ -41,7 +41,7 @@ class EventLoop {
|
||||
|
||||
void SetAcquiresPythonGIL();
|
||||
|
||||
void PushSetPaused(bool paused);
|
||||
void PushSetSuspended(bool suspended);
|
||||
|
||||
auto thread_id() const -> std::thread::id { return thread_id_; }
|
||||
|
||||
@ -81,11 +81,11 @@ class EventLoop {
|
||||
PushRunnableSynchronous(NewLambdaRunnableUnmanaged(lambda));
|
||||
}
|
||||
|
||||
/// Add a callback to be run on event-loop pauses.
|
||||
void AddPauseCallback(Runnable* runnable);
|
||||
/// Add a callback to be run on event-loop suspends.
|
||||
void AddSuspendCallback(Runnable* runnable);
|
||||
|
||||
/// Add a callback to be run on event-loop resumes.
|
||||
void AddResumeCallback(Runnable* runnable);
|
||||
/// Add a callback to be run on event-loop unsuspends.
|
||||
void AddUnsuspendCallback(Runnable* runnable);
|
||||
|
||||
auto has_pending_runnables() const -> bool { return !runnables_.empty(); }
|
||||
|
||||
@ -97,14 +97,14 @@ class EventLoop {
|
||||
/// the app through a flood of packets.
|
||||
auto CheckPushSafety() -> bool;
|
||||
|
||||
static auto GetStillPausingThreads() -> std::vector<EventLoop*>;
|
||||
static auto GetStillSuspendingEventLoops() -> std::vector<EventLoop*>;
|
||||
|
||||
auto paused() { return paused_; }
|
||||
auto suspended() { return suspended_; }
|
||||
auto done() -> bool { return done_; }
|
||||
|
||||
private:
|
||||
struct ThreadMessage_ {
|
||||
enum class Type { kShutdown = 999, kRunnable, kPause, kResume };
|
||||
enum class Type { kShutdown = 999, kRunnable, kSuspend, kUnsuspend };
|
||||
Type type;
|
||||
union {
|
||||
Runnable* runnable{};
|
||||
@ -147,8 +147,8 @@ class EventLoop {
|
||||
void PushThreadMessage_(const ThreadMessage_& t);
|
||||
|
||||
void RunPendingRunnables_();
|
||||
void RunPauseCallbacks_();
|
||||
void RunResumeCallbacks_();
|
||||
void RunSuspendCallbacks_();
|
||||
void RunUnsuspendCallbacks_();
|
||||
|
||||
void AcquireGIL_();
|
||||
void ReleaseGIL_();
|
||||
@ -156,10 +156,10 @@ class EventLoop {
|
||||
void BootstrapThread_();
|
||||
|
||||
bool writing_tally_{};
|
||||
bool paused_{};
|
||||
millisecs_t last_pause_time_{};
|
||||
int messages_since_paused_{};
|
||||
millisecs_t last_paused_message_report_time_{};
|
||||
bool suspended_{};
|
||||
millisecs_t last_suspend_time_{};
|
||||
int messages_since_suspended_{};
|
||||
millisecs_t last_suspended_message_report_time_{};
|
||||
bool done_{};
|
||||
ThreadSource source_;
|
||||
int listen_sd_{};
|
||||
@ -175,8 +175,8 @@ class EventLoop {
|
||||
|
||||
bool bootstrapped_{};
|
||||
std::list<std::pair<Runnable*, bool*>> runnables_;
|
||||
std::list<Runnable*> pause_callbacks_;
|
||||
std::list<Runnable*> resume_callbacks_;
|
||||
std::list<Runnable*> suspend_callbacks_;
|
||||
std::list<Runnable*> unsuspend_callbacks_;
|
||||
std::condition_variable thread_message_cv_;
|
||||
std::mutex thread_message_mutex_;
|
||||
std::list<ThreadMessage_> thread_messages_;
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
|
||||
#include "ballistica/core/platform/core_platform.h"
|
||||
#include "ballistica/core/support/base_soft.h"
|
||||
#include "ballistica/shared/foundation/event_loop.h"
|
||||
#include "ballistica/shared/foundation/logging.h"
|
||||
|
||||
namespace ballistica {
|
||||
|
||||
@ -199,6 +199,16 @@ void Object::ObjectThreadCheck() {
|
||||
|
||||
ThreadOwnership thread_ownership = GetThreadOwnership();
|
||||
|
||||
// Special case; graphics context (not simply a thread so have to handle
|
||||
// specially).
|
||||
if (thread_ownership == ThreadOwnership::kGraphicsContext) {
|
||||
if (!(g_base_soft && g_base_soft->InGraphicsContext())) {
|
||||
throw Exception("ObjectThreadCheck failed for " + GetObjectDescription()
|
||||
+ "; expected graphics context.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
EventLoopID t;
|
||||
if (thread_ownership == ThreadOwnership::kClassDefault) {
|
||||
t = GetDefaultOwnerThread();
|
||||
|
||||
@ -32,8 +32,12 @@ class Object {
|
||||
virtual auto GetObjectDescription() const -> std::string;
|
||||
|
||||
enum class ThreadOwnership {
|
||||
kClassDefault, // Uses class' GetDefaultOwnerThread() call.
|
||||
kNextReferencing // Uses whichever thread next acquires/accesses a ref.
|
||||
/// Uses class' GetDefaultOwnerThread() call.
|
||||
kClassDefault,
|
||||
/// Requires graphics context to be active.
|
||||
kGraphicsContext,
|
||||
/// Uses whichever thread next acquires/accesses a reference.
|
||||
kNextReferencing
|
||||
};
|
||||
|
||||
#if BA_DEBUG_BUILD
|
||||
|
||||
@ -97,6 +97,27 @@ enum class InputType {
|
||||
kLast // Sentinel
|
||||
};
|
||||
|
||||
// BA_EXPORT_PYTHON_ENUM
|
||||
/// Types of input a controller can send to the game.
|
||||
///
|
||||
/// Category: Enums
|
||||
///
|
||||
/// 'soft' may hide/reset the app but keep the process running, depending
|
||||
/// on the platform.
|
||||
///
|
||||
/// 'back' is a variant of 'soft' which may give 'back-button-pressed'
|
||||
/// behavior depending on the platform. (returning to some previous
|
||||
/// activity instead of dumping to the home screen, etc.)
|
||||
///
|
||||
/// 'hard' leads to the process exiting. This generally should be avoided
|
||||
/// on platforms such as mobile.
|
||||
enum class QuitType {
|
||||
kSoft,
|
||||
kBack,
|
||||
kHard,
|
||||
kLast // Sentinel.
|
||||
};
|
||||
|
||||
typedef int64_t TimerMedium;
|
||||
|
||||
// BA_EXPORT_PYTHON_ENUM
|
||||
|
||||
@ -9,7 +9,6 @@
|
||||
#include "ballistica/base/platform/base_platform.h"
|
||||
#include "ballistica/base/python/base_python.h"
|
||||
#include "ballistica/base/support/plus_soft.h"
|
||||
#include "ballistica/shared/foundation/event_loop.h"
|
||||
#include "ballistica/ui_v1/python/class/python_class_ui_mesh.h"
|
||||
#include "ballistica/ui_v1/python/class/python_class_ui_sound.h"
|
||||
#include "ballistica/ui_v1/python/class/python_class_ui_texture.h"
|
||||
|
||||
@ -4,9 +4,12 @@
|
||||
|
||||
#include "ballistica/base/assets/assets.h"
|
||||
#include "ballistica/base/audio/audio.h"
|
||||
#include "ballistica/base/input/device/keyboard_input.h"
|
||||
#include "ballistica/base/input/input.h"
|
||||
#include "ballistica/base/logic/logic.h"
|
||||
#include "ballistica/base/python/base_python.h"
|
||||
#include "ballistica/base/python/support/python_context_call.h"
|
||||
#include "ballistica/base/ui/dev_console.h"
|
||||
#include "ballistica/shared/foundation/event_loop.h"
|
||||
#include "ballistica/shared/python/python_command.h"
|
||||
#include "ballistica/shared/python/python_module_builder.h"
|
||||
@ -132,4 +135,27 @@ void UIV1Python::LaunchStringEditOld(TextWidget* w) {
|
||||
->Schedule(args);
|
||||
}
|
||||
|
||||
void UIV1Python::InvokeQuitWindow(QuitType quit_type) {
|
||||
assert(g_base->InLogicThread());
|
||||
base::ScopedSetContext ssc(nullptr);
|
||||
|
||||
// If the in-app console is active, dismiss it.
|
||||
if (auto* dev_console = g_base->ui->dev_console()) {
|
||||
if (dev_console->IsActive()) {
|
||||
dev_console->Dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
g_base->audio->PlaySound(g_base->assets->SysSound(base::SysSoundID::kSwish));
|
||||
auto py_enum = g_base->python->PyQuitType(quit_type);
|
||||
auto args = PythonRef::Stolen(Py_BuildValue("(O)", py_enum.Get()));
|
||||
objs().Get(UIV1Python::ObjID::kQuitWindowCall).Call(args);
|
||||
|
||||
// If we have a keyboard, give it UI ownership.
|
||||
base::InputDevice* keyboard = g_base->input->keyboard_input();
|
||||
if (keyboard) {
|
||||
g_base->ui->SetUIInputDevice(keyboard);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ballistica::ui_v1
|
||||
|
||||
@ -23,6 +23,7 @@ class UIV1Python {
|
||||
void ShowURL(const std::string& url);
|
||||
|
||||
static auto GetPyWidget(PyObject* o) -> Widget*;
|
||||
void InvokeQuitWindow(QuitType quit_type);
|
||||
|
||||
/// Specific Python objects we hold in objs_.
|
||||
enum class ObjID {
|
||||
|
||||
@ -3,14 +3,15 @@
|
||||
#include "ballistica/ui_v1/ui_v1.h"
|
||||
|
||||
#include "ballistica/base/app_mode/app_mode.h"
|
||||
#include "ballistica/base/audio/audio.h"
|
||||
#include "ballistica/base/graphics/component/empty_component.h"
|
||||
#include "ballistica/base/input/input.h"
|
||||
#include "ballistica/base/python/base_python.h"
|
||||
#include "ballistica/base/support/app_config.h"
|
||||
#include "ballistica/ui_v1/python/ui_v1_python.h"
|
||||
#include "ballistica/ui_v1/support/root_ui.h"
|
||||
#include "ballistica/ui_v1/widget/root_widget.h"
|
||||
#include "ballistica/ui_v1/widget/stack_widget.h"
|
||||
#include "ballistica/ui_v1/widget/text_widget.h"
|
||||
|
||||
namespace ballistica::ui_v1 {
|
||||
|
||||
@ -54,7 +55,7 @@ void UIV1FeatureSet::OnModuleExec(PyObject* module) {
|
||||
|
||||
// Let base know we exist.
|
||||
// (save it the trouble of trying to load us if it uses us passively).
|
||||
g_base->set_ui_v1(g_ui_v1);
|
||||
// g_base->set_ui_v1(g_ui_v1);
|
||||
|
||||
g_core->LifecycleLog("_bauiv1 exec end");
|
||||
}
|
||||
@ -72,11 +73,9 @@ void UIV1FeatureSet::DoHandleDeviceMenuPress(base::InputDevice* device) {
|
||||
|
||||
void UIV1FeatureSet::DoShowURL(const std::string& url) { python->ShowURL(url); }
|
||||
|
||||
void UIV1FeatureSet::DoQuitWindow() {
|
||||
g_ui_v1->python->objs().Get(UIV1Python::ObjID::kQuitWindowCall).Call();
|
||||
}
|
||||
|
||||
RootUI* UIV1FeatureSet::NewRootUI() { return new RootUI(); }
|
||||
// void UIV1FeatureSet::DoQuitWindow() {
|
||||
// g_ui_v1->python->objs().Get(UIV1Python::ObjID::kQuitWindowCall).Call();
|
||||
// }
|
||||
|
||||
bool UIV1FeatureSet::MainMenuVisible() {
|
||||
// We consider anything on our screen or overlay stacks to be a 'main menu'.
|
||||
@ -101,6 +100,7 @@ void UIV1FeatureSet::ActivatePartyIcon() {
|
||||
r->ActivatePartyIcon();
|
||||
}
|
||||
}
|
||||
|
||||
bool UIV1FeatureSet::PartyWindowOpen() {
|
||||
if (auto* r = root_ui()) {
|
||||
return r->party_window_open();
|
||||
@ -182,16 +182,16 @@ void UIV1FeatureSet::Draw(base::FrameDef* frame_def) {
|
||||
}
|
||||
}
|
||||
|
||||
void UIV1FeatureSet::OnAppStart() {
|
||||
void UIV1FeatureSet::OnActivate() {
|
||||
assert(g_base->InLogicThread());
|
||||
root_ui_ = g_base->ui_v1()->NewRootUI();
|
||||
if (root_ui_ == nullptr) {
|
||||
root_ui_ = new RootUI();
|
||||
}
|
||||
}
|
||||
void UIV1FeatureSet::OnDeactivate() { assert(g_base->InLogicThread()); }
|
||||
|
||||
void UIV1FeatureSet::Reset() {
|
||||
// Hmm; technically we don't need to recreate these each time we reset.
|
||||
root_widget_.Clear();
|
||||
|
||||
// Kill our screen-root widget.
|
||||
screen_root_widget_.Clear();
|
||||
|
||||
// (Re)create our screen-root widget.
|
||||
@ -252,9 +252,15 @@ void UIV1FeatureSet::OnScreenSizeChange() {
|
||||
}
|
||||
|
||||
void UIV1FeatureSet::OnLanguageChange() {
|
||||
// As well as existing UI stuff.
|
||||
if (auto* r = root_widget()) {
|
||||
r->OnLanguageChange();
|
||||
// Since switching languages is a bit costly, ignore redundant change
|
||||
// notifications. These will tend to happen nowadays since change
|
||||
// notifications go out anytime the ui-delegate switches.
|
||||
auto asset_language_state = g_base->assets->language_state();
|
||||
if (asset_language_state != language_state_) {
|
||||
language_state_ = asset_language_state;
|
||||
if (auto* r = root_widget()) {
|
||||
r->OnLanguageChange();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -282,6 +288,11 @@ void UIV1FeatureSet::DoApplyAppConfig() {
|
||||
base::AppConfig::BoolID::kAlwaysUseInternalKeyboard);
|
||||
}
|
||||
|
||||
auto UIV1FeatureSet::HasQuitConfirmDialog() -> bool { return true; }
|
||||
void UIV1FeatureSet::ConfirmQuit(QuitType quit_type) {
|
||||
python->InvokeQuitWindow(quit_type);
|
||||
}
|
||||
|
||||
UIV1FeatureSet::UILock::UILock(bool write) {
|
||||
assert(g_base->ui);
|
||||
assert(g_base->InLogicThread());
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
|
||||
#include <ballistica/base/input/device/input_device.h>
|
||||
|
||||
#include "ballistica/base/support/ui_v1_soft.h"
|
||||
#include "ballistica/base/ui/ui_delegate.h"
|
||||
#include "ballistica/shared/foundation/feature_set_native_component.h"
|
||||
|
||||
// Common header that most everything using our feature-set should include.
|
||||
@ -64,7 +64,7 @@ extern UIV1FeatureSet* g_ui_v1;
|
||||
/// Our C++ front-end to our feature set. This is what other C++
|
||||
/// feature-sets can 'Import' from us.
|
||||
class UIV1FeatureSet : public FeatureSetNativeComponent,
|
||||
public base::UIV1SoftInterface {
|
||||
public base::UIDelegateInterface {
|
||||
public:
|
||||
/// Instantiate our FeatureSet if needed and return the single instance of
|
||||
/// it. Basically a Python import statement.
|
||||
@ -85,8 +85,7 @@ class UIV1FeatureSet : public FeatureSetNativeComponent,
|
||||
static void OnModuleExec(PyObject* module);
|
||||
void DoHandleDeviceMenuPress(base::InputDevice* device) override;
|
||||
void DoShowURL(const std::string& url) override;
|
||||
void DoQuitWindow() override;
|
||||
auto NewRootUI() -> ui_v1::RootUI* override;
|
||||
// void DoQuitWindow() override;
|
||||
auto MainMenuVisible() -> bool override;
|
||||
auto PartyIconVisible() -> bool override;
|
||||
void ActivatePartyIcon() override;
|
||||
@ -101,7 +100,10 @@ class UIV1FeatureSet : public FeatureSetNativeComponent,
|
||||
assert(root_ui_);
|
||||
return root_ui_;
|
||||
}
|
||||
void OnAppStart() override;
|
||||
// void OnAppStart() override;
|
||||
void OnActivate() override;
|
||||
void OnDeactivate() override;
|
||||
|
||||
auto PartyWindowOpen() -> bool override;
|
||||
|
||||
// Return the root widget containing all windows & dialogs. Whenever this
|
||||
@ -134,6 +136,9 @@ class UIV1FeatureSet : public FeatureSetNativeComponent,
|
||||
return always_use_internal_on_screen_keyboard_;
|
||||
}
|
||||
|
||||
auto HasQuitConfirmDialog() -> bool override;
|
||||
void ConfirmQuit(QuitType quit_type) override;
|
||||
|
||||
private:
|
||||
UIV1FeatureSet();
|
||||
RootUI* root_ui_{};
|
||||
@ -142,6 +147,7 @@ class UIV1FeatureSet : public FeatureSetNativeComponent,
|
||||
Object::Ref<RootWidget> root_widget_;
|
||||
bool always_use_internal_on_screen_keyboard_{};
|
||||
int ui_lock_count_{};
|
||||
int language_state_{};
|
||||
};
|
||||
|
||||
} // namespace ballistica::ui_v1
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
#include "ballistica/ui_v1/widget/text_widget.h"
|
||||
|
||||
#include "ballistica/base/app_adapter/app_adapter.h"
|
||||
#include "ballistica/base/audio/audio.h"
|
||||
#include "ballistica/base/graphics/component/empty_component.h"
|
||||
#include "ballistica/base/graphics/component/simple_component.h"
|
||||
@ -13,11 +12,6 @@
|
||||
#include "ballistica/base/platform/base_platform.h"
|
||||
#include "ballistica/base/python/base_python.h"
|
||||
#include "ballistica/base/python/support/python_context_call.h"
|
||||
#include "ballistica/base/ui/ui.h"
|
||||
#include "ballistica/core/core.h"
|
||||
#include "ballistica/shared/foundation/event_loop.h"
|
||||
#include "ballistica/shared/foundation/logging.h"
|
||||
#include "ballistica/shared/foundation/types.h"
|
||||
#include "ballistica/shared/generic/utils.h"
|
||||
#include "ballistica/shared/python/python.h"
|
||||
#include "ballistica/shared/python/python_sys.h"
|
||||
|
||||
@ -69,6 +69,7 @@ values = [
|
||||
_error.SessionNotFoundError, # kSessionNotFoundError
|
||||
enums.TimeFormat, # kTimeFormatClass
|
||||
enums.TimeType, # kTimeTypeClass
|
||||
enums.QuitType, # kQuitTypeClass
|
||||
enums.InputType, # kInputTypeClass
|
||||
enums.Permission, # kPermissionClass
|
||||
enums.SpecialChar, # kSpecialCharClass
|
||||
|
||||
@ -14,8 +14,8 @@ values = [
|
||||
app.push_apply_app_config, # kAppPushApplyAppConfigCall
|
||||
app.on_native_start, # kAppOnNativeStartCall
|
||||
app.on_native_bootstrapping_complete, # kAppOnNativeBootstrappingCompleteCall
|
||||
app.on_native_pause, # kAppOnNativePauseCall
|
||||
app.on_native_resume, # kAppOnNativeResumeCall
|
||||
app.on_native_suspend, # kAppOnNativeSuspendCall
|
||||
app.on_native_unsuspend, # kAppOnNativeUnsuspendCall
|
||||
app.on_native_shutdown, # kAppOnNativeShutdownCall
|
||||
app.on_native_shutdown_complete, # kAppOnNativeShutdownCompleteCall
|
||||
app.read_config, # kAppReadConfigCall
|
||||
|
||||
@ -48,7 +48,7 @@ def _gen_lprop_file(local_properties_path: str) -> str:
|
||||
if not found:
|
||||
home = os.getenv('HOME')
|
||||
assert home is not None
|
||||
test_paths = [home + '/Library/Android/sdk']
|
||||
test_paths = [home + '/Library/Android/sdk', home + '/AndroidSDK']
|
||||
for sdk_dir in test_paths:
|
||||
if os.path.exists(sdk_dir):
|
||||
found = True
|
||||
@ -58,12 +58,10 @@ def _gen_lprop_file(local_properties_path: str) -> str:
|
||||
assert sdk_dir is not None
|
||||
if not found:
|
||||
if not os.path.exists(sdk_dir):
|
||||
print(
|
||||
'ERROR: Android sdk not found; install '
|
||||
'the android sdk and try again',
|
||||
file=sys.stderr,
|
||||
raise CleanError(
|
||||
f'Android sdk not found at {sdk_dir}; install '
|
||||
'the android sdk and try again.',
|
||||
)
|
||||
sys.exit(255)
|
||||
config = (
|
||||
'\n# This file was automatically generated by '
|
||||
+ os.path.abspath(sys.argv[0])
|
||||
|
||||
@ -43,8 +43,8 @@ class PyRequirement:
|
||||
# remove our custom module based stuff soon if nobody complains, which
|
||||
# would free us to theoretically move to a requirements.txt based setup.
|
||||
PY_REQUIREMENTS = [
|
||||
PyRequirement(pipname='pylint', minversion=[3, 0, 0]),
|
||||
PyRequirement(pipname='mypy', minversion=[1, 5, 1]),
|
||||
PyRequirement(pipname='pylint', minversion=[3, 0, 1]),
|
||||
PyRequirement(pipname='mypy', minversion=[1, 6, 0]),
|
||||
PyRequirement(pipname='cpplint', minversion=[1, 6, 1]),
|
||||
PyRequirement(pipname='pytest', minversion=[7, 4, 2]),
|
||||
PyRequirement(pipname='pytz', minversion=[2023, 3]),
|
||||
@ -706,7 +706,7 @@ def cmake_prep_dir(dirname: str, verbose: bool = False) -> None:
|
||||
# away all cmake builds everywhere (to keep things clean if we
|
||||
# rename or move something in the build dir or if we change
|
||||
# something cmake doesn't properly handle without a fresh start).
|
||||
entries: list[Entry] = [Entry('explicit cmake rebuild', '3')]
|
||||
entries: list[Entry] = [Entry('explicit cmake rebuild', '4')]
|
||||
|
||||
# Start fresh if cmake version changes.
|
||||
cmake_ver_output = subprocess.run(
|
||||
|
||||
@ -124,6 +124,7 @@ class ResourcesMakefileGenerator:
|
||||
self._add_apple_tv_3d_icon()
|
||||
self._add_apple_tv_store_icon()
|
||||
self._add_google_vr_icon()
|
||||
self._add_macos_cursor()
|
||||
our_lines_private_2 = (
|
||||
['# __PUBSYNC_STRIP_BEGIN__']
|
||||
+ _empty_line_if(bool(self.targets))
|
||||
@ -570,6 +571,33 @@ class ResourcesMakefileGenerator:
|
||||
)
|
||||
self.targets.append(Target(src=[src], dst=dst, cmd=cmd, mkdir=True))
|
||||
|
||||
def _add_macos_cursor(self) -> None:
|
||||
sizes = [
|
||||
(64, 1),
|
||||
(64, 2),
|
||||
]
|
||||
for size in sizes:
|
||||
res = int(size[0] * size[1])
|
||||
src = os.path.join('cursor.png')
|
||||
dst = os.path.join(
|
||||
ROOT_DIR,
|
||||
f'{self.namel}-xcode',
|
||||
f'{self.nameu} Shared',
|
||||
'Assets.xcassets',
|
||||
'Cursor macOS.imageset',
|
||||
'cursor_' + str(size[0]) + 'x' + str(size[1]) + '.png',
|
||||
)
|
||||
cmd = ' '.join(
|
||||
[
|
||||
RESIZE_CMD,
|
||||
str(res),
|
||||
str(res),
|
||||
'"' + src + '"',
|
||||
'"' + dst + '"',
|
||||
]
|
||||
)
|
||||
self.targets.append(Target(src=[src], dst=dst, cmd=cmd))
|
||||
|
||||
|
||||
def _empty_line_if(condition: bool) -> list[str]:
|
||||
return [''] if condition else []
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user