diff --git a/.efrocachemap b/.efrocachemap
index b7a571d2..b24be883 100644
--- a/.efrocachemap
+++ b/.efrocachemap
@@ -4056,50 +4056,50 @@
"build/assets/windows/Win32/ucrtbased.dll": "2def5335207d41b21b9823f6805997f1",
"build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae",
"build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599",
- "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "ad13d636bcb25150044a7644846b8a09",
- "build/prefab/full/linux_arm64_gui/release/ballisticakit": "7c5df955611590ef491bf614fbd60179",
- "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "ae38bd212ae64b51482a2ccb9c1cbfd3",
- "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "d0bcee2dd5567719aa35667c5206dffc",
- "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "eb3cd4f86175afcf8ffa2749afa32fa3",
- "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "6b86ba36c3719773008feaa6cdc0d0f8",
- "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "877c9ae4532fef809a3dcbd8ffea343c",
- "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "7df313c48c87460f56fa837502965088",
- "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "bc40bb549d26437fb8679c1e9d088272",
- "build/prefab/full/mac_arm64_gui/release/ballisticakit": "47aa08cb9f5e660023f0f3c0e4ffd65e",
- "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "bbb0a8383d6ce1ca887190ea49223f4f",
- "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "7dd91e3407d49981c1c975d4f01ac205",
- "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "c87883aa2f832e792e945fd9208d712a",
- "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "fea8fd84d8c060f2f82f402902b8c54e",
- "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "0a454a8be47f37231655761d15e3f7e5",
- "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "4c79db3a882eb0b8b225a8df0339b1cc",
- "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "43b9ef321f8e80da29ddb19a760dbd77",
- "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "6f891004f2f07c452dea29bd53f29d30",
- "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "bf7a1ce0e7a2015d538406c6f6df761c",
- "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "cf213dce81901a67c9970b3befdaa320",
- "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "473e7e6c0cf90b9e6ac653552b18f68d",
- "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "4e11b895cbf2e1339cf34bc06c54a4ea",
- "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "473e7e6c0cf90b9e6ac653552b18f68d",
- "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "4e11b895cbf2e1339cf34bc06c54a4ea",
- "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "d9af1a429cff9346e0cad6fcea017e5b",
- "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "ae5f87286947575463c386cfe1c443e4",
- "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "d9af1a429cff9346e0cad6fcea017e5b",
- "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "ae5f87286947575463c386cfe1c443e4",
- "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "110eef3dc285a35a1899510e368c73b1",
- "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "2692dc69f7cb2501f0aaa8675f559987",
- "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "110eef3dc285a35a1899510e368c73b1",
- "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "2692dc69f7cb2501f0aaa8675f559987",
- "build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "344954c4f788d7d9b4d7035ebb6131d8",
- "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "48c4873dae2344c1d4092a1d85dab424",
- "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "abcede4e60fa8877f18e66e086fb7387",
- "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "48c4873dae2344c1d4092a1d85dab424",
- "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "6149911c660a9864b651cc1a8e50eec1",
- "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "57cef68ab703ba819bd0fbe9e4b1c331",
- "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "a47bab28b86c7cefce891b8e5c8b687a",
- "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "d68ebb1139363d711b044de65e17b204",
- "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "ea1349137f64f3d662b9a95278ca4c02",
- "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "c8731ff226716cee3d1e46027ead1cfe",
- "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "4ee6b633a99c5bcbea4f5dee5bda186e",
- "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "2c3bd4952b30d88247229ad309f73092",
+ "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "c98fdb967f44411171c3d7fde1c74471",
+ "build/prefab/full/linux_arm64_gui/release/ballisticakit": "5514854ae459adab968120a361692cd5",
+ "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "83bcfb3af22b61e5c35972d989b3f7f8",
+ "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "a63758734d93eb969fe482b56c8d1ed2",
+ "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "7e68a605645c70fca06e4f4d5155bb0c",
+ "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "0598aa59718bb8404c3b007c22123c75",
+ "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "9c86d900fc1bc2d6f3bdb4c3e26149da",
+ "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "37f5996151f983b45925ad15dce2d0e9",
+ "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "b8560855bf772b3e74f2a8f190f54885",
+ "build/prefab/full/mac_arm64_gui/release/ballisticakit": "55c777fa3afd720089223fb949478502",
+ "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "81a41ddca9da9060d5d5034a84845674",
+ "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "536825d983f4a3bcec018b0295005bdb",
+ "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "1733a9610de15f77dd8df3effcca1516",
+ "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "f7434d47c371f0baaae676d288847351",
+ "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "d16ea39c50f41117697789dafb249200",
+ "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "55d18233c5b887af17afcb266a8c5597",
+ "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "c4df3b76ef890daf48e8181cffd4cd82",
+ "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "eb609d02a0897c671e40519faad3365b",
+ "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "0c0d303a440e852d0112c3a5aa75ef36",
+ "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "bdd6b2c15d6718a6c99ee287d965f022",
+ "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "becf7a70c7c0d7bb5bfe731dde5e0249",
+ "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "c9ef036408f0832cd068a34365485e0b",
+ "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "becf7a70c7c0d7bb5bfe731dde5e0249",
+ "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "c9ef036408f0832cd068a34365485e0b",
+ "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "5bd8bcd68e03939501d192b7cda55d64",
+ "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "a8545c0c985eef9219c351773c5c6127",
+ "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "5bd8bcd68e03939501d192b7cda55d64",
+ "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "a8545c0c985eef9219c351773c5c6127",
+ "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "1e2c088713c47ff47d9aa312ebb0bd1a",
+ "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "e6bbe6c564c65cb498edc58daec1e084",
+ "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "1e2c088713c47ff47d9aa312ebb0bd1a",
+ "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "e6bbe6c564c65cb498edc58daec1e084",
+ "build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "98df65aba607e74eb5c0c7305903ac29",
+ "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "2b57cf28eeadf43d09b1d780a5db1423",
+ "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "3314d791a9ab37ea81be824460c63d14",
+ "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "2b57cf28eeadf43d09b1d780a5db1423",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "bd63402d48fce829f16d59c6c1f87977",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "de24c4e6f661f6201b933af3343084cc",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "84904f537e435d09c06b4b6c10abea7d",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "22a32d161e85baa6c6459412a368bf82",
+ "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "f43972d496e1953fdc30ff094a22a0d1",
+ "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "c069093fb4773f3feac13236d474ecf1",
+ "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "4d745c03bbeab02c5f69bed1ae376933",
+ "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "c1e1321bb0d5bb74211377e0f5cae45c",
"src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c",
"src/assets/ba_data/python/babase/_mgen/enums.py": "28323912b56ec07701eda3d41a6a4101",
"src/ballistica/base/mgen/pyembed/binding_base.inc": "6df0f34207346d89a72924249ddd4706",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 46e6c34d..9d37890f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,4 @@
-### 1.7.28 (build 21491, api 8, 2023-10-22)
+### 1.7.28 (build 21510, api 8, 2023-10-26)
- 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
@@ -154,6 +154,16 @@
seems pretty awesome these days. It should support most stuff SDL does and
with less configuring involved. Please holler if you come across something
that doesn't work.
+- Mac build is also now using the Game Controller Framework to handle keyboard
+ events. This should better handle things like modifier keys and also will
+ allow us to use that exact same code on the iPad/iPhone version.
+- OS key repeat events are no longer passed through the engine. This means that
+ any time we want repeating behavior, such as holding an arrow key to move
+ through UI elements, we will need to wire it up ourselves. We already do this
+ for things like game controllers however, so this is more consistent in a way.
+- Dev console no longer claims key events unless the Python tab is showing and
+ there is a hardware keyboard attached. This allows showing dev console tabs
+ above gameplay without interfering with it.
### 1.7.27 (build 21282, api 8, 2023-08-30)
diff --git a/ballisticakit-cmake/CMakeLists.txt b/ballisticakit-cmake/CMakeLists.txt
index 297cfe0b..13214011 100644
--- a/ballisticakit-cmake/CMakeLists.txt
+++ b/ballisticakit-cmake/CMakeLists.txt
@@ -442,6 +442,8 @@ set(BALLISTICA_SOURCES
${BA_SRC_ROOT}/ballistica/base/support/huffman.cc
${BA_SRC_ROOT}/ballistica/base/support/huffman.h
${BA_SRC_ROOT}/ballistica/base/support/plus_soft.h
+ ${BA_SRC_ROOT}/ballistica/base/support/repeater.cc
+ ${BA_SRC_ROOT}/ballistica/base/support/repeater.h
${BA_SRC_ROOT}/ballistica/base/support/stdio_console.cc
${BA_SRC_ROOT}/ballistica/base/support/stdio_console.h
${BA_SRC_ROOT}/ballistica/base/support/stress_test.cc
diff --git a/ballisticakit-windows/Generic/BallisticaKitGeneric.vcxproj b/ballisticakit-windows/Generic/BallisticaKitGeneric.vcxproj
index affcd239..68e39f6f 100644
--- a/ballisticakit-windows/Generic/BallisticaKitGeneric.vcxproj
+++ b/ballisticakit-windows/Generic/BallisticaKitGeneric.vcxproj
@@ -434,6 +434,8 @@
+
+
diff --git a/ballisticakit-windows/Generic/BallisticaKitGeneric.vcxproj.filters b/ballisticakit-windows/Generic/BallisticaKitGeneric.vcxproj.filters
index b3e5ebdf..761cf81f 100644
--- a/ballisticakit-windows/Generic/BallisticaKitGeneric.vcxproj.filters
+++ b/ballisticakit-windows/Generic/BallisticaKitGeneric.vcxproj.filters
@@ -736,6 +736,12 @@
ballistica\base\support
+
+ ballistica\base\support
+
+
+ ballistica\base\support
+
ballistica\base\support
diff --git a/ballisticakit-windows/Headless/BallisticaKitHeadless.vcxproj b/ballisticakit-windows/Headless/BallisticaKitHeadless.vcxproj
index 82d010b3..02f49c4b 100644
--- a/ballisticakit-windows/Headless/BallisticaKitHeadless.vcxproj
+++ b/ballisticakit-windows/Headless/BallisticaKitHeadless.vcxproj
@@ -429,6 +429,8 @@
+
+
diff --git a/ballisticakit-windows/Headless/BallisticaKitHeadless.vcxproj.filters b/ballisticakit-windows/Headless/BallisticaKitHeadless.vcxproj.filters
index b3e5ebdf..761cf81f 100644
--- a/ballisticakit-windows/Headless/BallisticaKitHeadless.vcxproj.filters
+++ b/ballisticakit-windows/Headless/BallisticaKitHeadless.vcxproj.filters
@@ -736,6 +736,12 @@
ballistica\base\support
+
+ ballistica\base\support
+
+
+ ballistica\base\support
+
ballistica\base\support
diff --git a/src/assets/ba_data/python/babase/_app.py b/src/assets/ba_data/python/babase/_app.py
index ec0e0d8b..c22aee9f 100644
--- a/src/assets/ba_data/python/babase/_app.py
+++ b/src/assets/ba_data/python/babase/_app.py
@@ -915,6 +915,12 @@ class App:
_babase.lifecyclelog('fade-and-shutdown-graphics begin')
_babase.fade_screen(False, time=0.15)
await asyncio.sleep(0.15)
+
+ # Now tell the graphics system to go down and wait until
+ # it has done so.
+ _babase.graphics_shutdown_begin()
+ while not _babase.graphics_shutdown_is_complete():
+ await asyncio.sleep(0.01)
_babase.lifecyclelog('fade-and-shutdown-graphics end')
async def _fade_and_shutdown_audio(self) -> None:
diff --git a/src/assets/ba_data/python/baenv.py b/src/assets/ba_data/python/baenv.py
index 0d19c817..d2bc7bfe 100644
--- a/src/assets/ba_data/python/baenv.py
+++ b/src/assets/ba_data/python/baenv.py
@@ -52,7 +52,7 @@ if TYPE_CHECKING:
# Build number and version of the ballistica binary we expect to be
# using.
-TARGET_BALLISTICA_BUILD = 21491
+TARGET_BALLISTICA_BUILD = 21510
TARGET_BALLISTICA_VERSION = '1.7.28'
diff --git a/src/ballistica/base/app_adapter/app_adapter.cc b/src/ballistica/base/app_adapter/app_adapter.cc
index a7f1cf89..ee84b319 100644
--- a/src/ballistica/base/app_adapter/app_adapter.cc
+++ b/src/ballistica/base/app_adapter/app_adapter.cc
@@ -315,4 +315,7 @@ auto AppAdapter::GetGraphicsClientContext() -> GraphicsClientContext* {
return new GraphicsClientContext();
}
+auto AppAdapter::GetKeyRepeatDelay() -> float { return 0.3f; }
+auto AppAdapter::GetKeyRepeatInterval() -> float { return 0.08f; }
+
} // namespace ballistica::base
diff --git a/src/ballistica/base/app_adapter/app_adapter.h b/src/ballistica/base/app_adapter/app_adapter.h
index 25fb83de..829c236d 100644
--- a/src/ballistica/base/app_adapter/app_adapter.h
+++ b/src/ballistica/base/app_adapter/app_adapter.h
@@ -189,6 +189,13 @@ class AppAdapter {
/// changing (it may be preferable to rely on dialogs for non-english
/// languages/etc.). Default implementation returns false. This function
/// should be callable from any thread.
+ ///
+ /// Note that UI elements wanting to accept direct keyboard input should
+ /// not call this directly, but instead should call
+ /// UI::UIHasDirectKeyboardInput, as that takes into account other factors
+ /// such as which device is currently controlling the UI (Someone
+ /// navigating the UI with a game controller may still get an on-screen
+ /// keyboard even if there is a physical keyboard attached).
virtual auto HasDirectKeyboardInput() -> bool;
/// Called in the graphics context to apply new settings coming in from
@@ -197,6 +204,9 @@ class AppAdapter {
/// settings coming in.
virtual void ApplyGraphicsSettings(const GraphicsSettings* settings);
+ virtual auto GetKeyRepeatDelay() -> float;
+ virtual auto GetKeyRepeatInterval() -> float;
+
protected:
AppAdapter();
virtual ~AppAdapter();
diff --git a/src/ballistica/base/app_adapter/app_adapter_apple.cc b/src/ballistica/base/app_adapter/app_adapter_apple.cc
index aaefa65e..737b89bf 100644
--- a/src/ballistica/base/app_adapter/app_adapter_apple.cc
+++ b/src/ballistica/base/app_adapter/app_adapter_apple.cc
@@ -3,16 +3,21 @@
#include "ballistica/base/app_adapter/app_adapter_apple.h"
-#include
-
#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/platform/apple/from_swift.h"
#include "ballistica/base/support/app_config.h"
#include "ballistica/shared/ballistica.h"
#include "ballistica/shared/foundation/event_loop.h"
+// clang-format off
+// This needs to be below ballistica headers since it relies on
+// some types in them but does not include headers itself.
+#include
+// clang-format on
+
namespace ballistica::base {
/// RAII-friendly way to mark the thread and calls we're allowed to run graphics
@@ -41,7 +46,7 @@ auto AppAdapterApple::ManagesMainThreadEventLoop() const -> bool {
void AppAdapterApple::DoPushMainThreadRunnable(Runnable* runnable) {
// Kick this along to swift.
- BallisticaKit::FromCppPushRawRunnableToMain(runnable);
+ BallisticaKit::FromCpp::PushRawRunnableToMain(runnable);
}
void AppAdapterApple::DoApplyAppConfig() { assert(g_base->InLogicThread()); }
@@ -122,16 +127,16 @@ auto AppAdapterApple::TryRender() -> bool {
// matches what we have (or until we try for too long or fail at drawing).
seconds_t start_time = g_core->GetAppTimeSeconds();
for (int i = 0; i < 5; ++i) {
- if (((std::abs(resize_target_resolution_.x
+ bool size_differs =
+ ((std::abs(resize_target_resolution_.x
- g_base->graphics_server->screen_pixel_width())
> 0.01f)
|| (std::abs(resize_target_resolution_.y
- g_base->graphics_server->screen_pixel_height())
- > 0.01f))
- && g_core->GetAppTimeSeconds() - start_time < 0.1 && result) {
+ > 0.01f));
+ if (size_differs && g_core->GetAppTimeSeconds() - start_time < 0.1
+ && result) {
result = g_base->graphics_server->TryRender();
- } else {
- break;
}
}
}
@@ -182,13 +187,13 @@ void AppAdapterApple::SetHardwareCursorVisible(bool visible) {
assert(g_core->InMainThread());
#if BA_OSTYPE_MACOS
- BallisticaKit::CocoaFromCppSetCursorVisible(visible);
+ BallisticaKit::CocoaFromCpp::SetCursorVisible(visible);
#endif
}
void AppAdapterApple::TerminateApp() {
#if BA_OSTYPE_MACOS
- BallisticaKit::CocoaFromCppTerminateApp();
+ BallisticaKit::CocoaFromCpp::TerminateApp();
#else
AppAdapter::TerminateApp();
#endif
@@ -205,7 +210,7 @@ auto AppAdapterApple::FullscreenControlAvailable() const -> bool {
auto AppAdapterApple::FullscreenControlGet() const -> bool {
#if BA_OSTYPE_MACOS
- return BallisticaKit::CocoaFromCppGetMainWindowIsFullscreen();
+ return BallisticaKit::CocoaFromCpp::GetMainWindowIsFullscreen();
#else
return false;
#endif
@@ -213,7 +218,7 @@ auto AppAdapterApple::FullscreenControlGet() const -> bool {
void AppAdapterApple::FullscreenControlSet(bool fullscreen) {
#if BA_OSTYPE_MACOS
- return BallisticaKit::CocoaFromCppSetMainWindowFullscreen(fullscreen);
+ return BallisticaKit::CocoaFromCpp::SetMainWindowFullscreen(fullscreen);
#endif
}
@@ -224,6 +229,22 @@ auto AppAdapterApple::FullscreenControlKeyShortcut() const
auto AppAdapterApple::HasDirectKeyboardInput() -> bool { return true; };
+auto AppAdapterApple::GetKeyRepeatDelay() -> float {
+#if BA_OSTYPE_MACOS
+ return BallisticaKit::CocoaFromCpp::GetKeyRepeatDelay();
+#else
+ return AppAdapter::GetKeyRepeatDelay();
+#endif
+}
+
+auto AppAdapterApple::GetKeyRepeatInterval() -> float {
+#if BA_OSTYPE_MACOS
+ return BallisticaKit::CocoaFromCpp::GetKeyRepeatInterval();
+#else
+ return AppAdapter::GetKeyRepeatDelay();
+#endif
+}
+
} // namespace ballistica::base
#endif // BA_XCODE_BUILD
diff --git a/src/ballistica/base/app_adapter/app_adapter_apple.h b/src/ballistica/base/app_adapter/app_adapter_apple.h
index 9217299b..d1c9823d 100644
--- a/src/ballistica/base/app_adapter/app_adapter_apple.h
+++ b/src/ballistica/base/app_adapter/app_adapter_apple.h
@@ -41,6 +41,9 @@ class AppAdapterApple : public AppAdapter {
auto HasDirectKeyboardInput() -> bool override;
void EnableResizeFriendlyMode(int width, int height);
+ auto GetKeyRepeatDelay() -> float override;
+ auto GetKeyRepeatInterval() -> float override;
+
protected:
void DoPushMainThreadRunnable(Runnable* runnable) override;
void DoPushGraphicsContextRunnable(Runnable* runnable) override;
diff --git a/src/ballistica/base/app_adapter/app_adapter_sdl.cc b/src/ballistica/base/app_adapter/app_adapter_sdl.cc
index b43f8397..6def0c9f 100644
--- a/src/ballistica/base/app_adapter/app_adapter_sdl.cc
+++ b/src/ballistica/base/app_adapter/app_adapter_sdl.cc
@@ -415,7 +415,9 @@ void AppAdapterSDL::HandleSDLEvent_(const SDL_Event& event) {
}
case SDL_KEYDOWN: {
- g_base->input->PushKeyPressEvent(event.key.keysym);
+ if (!event.key.repeat) {
+ g_base->input->PushKeyPressEvent(event.key.keysym);
+ }
break;
}
diff --git a/src/ballistica/base/assets/assets_server.cc b/src/ballistica/base/assets/assets_server.cc
index 0fddc71f..102c6027 100644
--- a/src/ballistica/base/assets/assets_server.cc
+++ b/src/ballistica/base/assets/assets_server.cc
@@ -26,7 +26,7 @@ void AssetsServer::OnAppStartInThread() {
// Ask our thread to give us periodic processing time (close to but
// not *exactly* one second; try to avoid aliasing with similar updates).
process_timer_ = event_loop()->NewTimer(
- 987, true, NewLambdaRunnable([this] { Process(); }));
+ 987, true, NewLambdaRunnable([this] { Process(); }).Get());
}
void AssetsServer::PushPendingPreload(Object::Ref* asset_ref_ptr) {
diff --git a/src/ballistica/base/assets/texture_asset.cc b/src/ballistica/base/assets/texture_asset.cc
index aa757cb4..8198a4d9 100644
--- a/src/ballistica/base/assets/texture_asset.cc
+++ b/src/ballistica/base/assets/texture_asset.cc
@@ -15,7 +15,7 @@
namespace ballistica::base {
-static void rgba8888_unpremultiply_in_place(uint8_t* src, size_t cb) {
+static void Rgba8888UnpremultiplyInPlace_(uint8_t* src, size_t cb) {
// Compute the actual number of pixel elements in the buffer.
size_t cpel = cb / 4;
auto* psrc = src;
@@ -157,7 +157,7 @@ void TextureAsset::DoPreload() {
auto* buffer = static_cast(malloc(buffer_size));
preload_datas_[0].buffers[0] = buffer;
memcpy(buffer, pixels, buffer_size);
- rgba8888_unpremultiply_in_place(buffer, buffer_size);
+ Rgba8888UnpremultiplyInPlace_(buffer, buffer_size);
preload_datas_[0].widths[0] = width;
preload_datas_[0].heights[0] = height;
preload_datas_[0].formats[0] = TextureFormat::kRGBA_8888;
diff --git a/src/ballistica/base/audio/audio_server.cc b/src/ballistica/base/audio/audio_server.cc
index 1e873026..adcdee40 100644
--- a/src/ballistica/base/audio/audio_server.cc
+++ b/src/ballistica/base/audio/audio_server.cc
@@ -161,7 +161,7 @@ void AudioServer::OnAppStartInThread_() {
// Get our thread to give us periodic processing time.
process_timer_ =
event_loop()->NewTimer(kAudioProcessIntervalNormal, true,
- NewLambdaRunnable([this] { Process_(); }));
+ NewLambdaRunnable([this] { Process_(); }).Get());
#if BA_ENABLE_AUDIO
diff --git a/src/ballistica/base/base.h b/src/ballistica/base/base.h
index 473e5edf..07ab3f2e 100644
--- a/src/ballistica/base/base.h
+++ b/src/ballistica/base/base.h
@@ -99,6 +99,7 @@ class RenderPass;
class RenderTarget;
class RemoteAppServer;
class RemoteControlInput;
+class Repeater;
class ScoreToBeat;
class AppAdapterSDL;
class SDLContext;
diff --git a/src/ballistica/base/graphics/graphics.cc b/src/ballistica/base/graphics/graphics.cc
index cc01e49f..7f405a9d 100644
--- a/src/ballistica/base/graphics/graphics.cc
+++ b/src/ballistica/base/graphics/graphics.cc
@@ -182,7 +182,7 @@ void Graphics::UpdateInitialGraphicsSettingsSend_() {
void Graphics::StepDisplayTime() { assert(g_base->InLogicThread()); }
void Graphics::AddCleanFrameCommand(const Object::Ref& c) {
- BA_PRECONDITION(g_base->InLogicThread());
+ assert(g_base->InLogicThread());
clean_frame_commands_.push_back(c);
}
@@ -1058,7 +1058,7 @@ void Graphics::ClearFrameDefDeleteList() {
}
void Graphics::FadeScreen(bool to, millisecs_t time, PyObject* endcall) {
- BA_PRECONDITION(g_base->InLogicThread());
+ assert(g_base->InLogicThread());
// If there's an ourstanding fade-end command, go ahead and run it.
// (otherwise, overlapping fades can cause things to get lost)
if (fade_end_call_.Exists()) {
@@ -1186,7 +1186,7 @@ void Graphics::DrawDevUI(FrameDef* frame_def) {
void Graphics::BuildAndPushFrameDef() {
assert(g_base->InLogicThread());
- BA_PRECONDITION_FATAL(g_base->logic->app_bootstrapping_complete());
+ assert(g_base->logic->app_bootstrapping_complete());
assert(camera_.Exists());
assert(!g_core->HeadlessMode());
diff --git a/src/ballistica/base/graphics/graphics.h b/src/ballistica/base/graphics/graphics.h
index f04ff79c..4c244dd3 100644
--- a/src/ballistica/base/graphics/graphics.h
+++ b/src/ballistica/base/graphics/graphics.h
@@ -141,8 +141,8 @@ class Graphics {
static void DrawRadialMeter(MeshIndexedSimpleFull* m, float amt);
- // Ways to add a few simple component types quickly.
- // (uses particle rendering for efficient batches).
+ // Ways to add a few simple component types quickly (uses particle
+ // rendering for efficient batches).
void DrawBlotch(const Vector3f& pos, float size, float r, float g, float b,
float a) {
DoDrawBlotch(&blotch_indices_, &blotch_verts_, pos, size, r, g, b, a);
@@ -240,13 +240,8 @@ class Graphics {
float upper_top);
void ReleaseFadeEndCommand();
- // auto tv_border() const {
- // assert(g_base->InLogicThread());
- // return tv_border_;
- // }
-
- // Nodes that draw flat stuff into the overlay pass should query this z value
- // for where to draw in z.
+ // Nodes that draw flat stuff into the overlay pass should query this z
+ // value for where to draw in z.
auto overlay_node_z_depth() {
fetched_overlay_node_z_depth_ = true;
return overlay_node_z_depth_;
@@ -296,8 +291,8 @@ class Graphics {
void AddMeshDataCreate(MeshData* d);
void AddMeshDataDestroy(MeshData* d);
- // For debugging: ensures that only transparent or opaque components
- // are submitted while enabled.
+ // For debugging: ensures that only transparent or opaque components are
+ // submitted while enabled.
auto drawing_transparent_only() const { return drawing_transparent_only_; }
void set_drawing_transparent_only(bool val) {
drawing_transparent_only_ = val;
@@ -362,8 +357,8 @@ class Graphics {
}
/// For temporary use in arbitrary threads. This should be removed when
- /// possible and replaced with proper safe thread-specific access
- /// patterns (so we can support switching renderers/etc.).
+ /// possible and replaced with proper safe thread-specific access patterns
+ /// (so we can support switching renderers/etc.).
auto placeholder_client_context() const -> const GraphicsClientContext* {
// Using this from arbitrary threads is currently ok currently since
// context never changes once set. Will need to kill this call once that
@@ -478,18 +473,18 @@ class Graphics {
float shadow_lower_top_{4.0f};
float shadow_upper_bottom_{30.0f};
float shadow_upper_top_{40.0f};
+ seconds_t last_cursor_visibility_event_time_{};
millisecs_t fade_start_{};
millisecs_t fade_time_{};
millisecs_t next_stat_update_time_{};
millisecs_t progress_bar_end_time_{-9999};
millisecs_t last_progress_bar_draw_time_{};
millisecs_t last_progress_bar_start_time_{};
- microsecs_t last_suppress_gyro_time_{};
- seconds_t last_cursor_visibility_event_time_{};
- microsecs_t next_frame_number_filtered_increment_time_{};
- microsecs_t last_create_frame_def_time_microsecs_{};
millisecs_t last_create_frame_def_time_millisecs_{};
millisecs_t last_jitter_update_time_{};
+ microsecs_t last_suppress_gyro_time_{};
+ microsecs_t next_frame_number_filtered_increment_time_{};
+ microsecs_t last_create_frame_def_time_microsecs_{};
Object::Ref screen_mesh_;
Object::Ref progress_bar_bottom_mesh_;
Object::Ref progress_bar_top_mesh_;
diff --git a/src/ballistica/base/graphics/graphics_server.cc b/src/ballistica/base/graphics/graphics_server.cc
index 98351f47..949efced 100644
--- a/src/ballistica/base/graphics/graphics_server.cc
+++ b/src/ballistica/base/graphics/graphics_server.cc
@@ -137,14 +137,10 @@ auto GraphicsServer::TryRender() -> bool {
auto GraphicsServer::WaitForRenderFrameDef_() -> FrameDef* {
assert(g_base->app_adapter->InGraphicsContext());
- millisecs_t app_time = g_core->GetAppTimeMillisecs();
+ millisecs_t start_time = g_core->GetAppTimeMillisecs();
- if (!renderer_) {
- return nullptr;
- }
-
- // If the app is paused, never render.
- if (g_base->app_adapter->app_suspended()) {
+ // Don't bother waiting if we can't/shouldn't render anyway.
+ if (!renderer_ || shutting_down_ || g_base->app_adapter->app_suspended()) {
return nullptr;
}
@@ -173,12 +169,12 @@ auto GraphicsServer::WaitForRenderFrameDef_() -> FrameDef* {
// if we've been waiting for too long, give up. On some platforms such
// as Android, this frame will still get flipped whether we draw in it
// or not, so we *really* want to not skip drawing if we can help it.
- millisecs_t t = g_core->GetAppTimeMillisecs() - app_time;
+ millisecs_t t = g_core->GetAppTimeMillisecs() - start_time;
if (t >= 1000) {
if (g_buildconfig.debug_build()) {
Log(LogLevel::kWarning,
- "GraphicsServer: aborting GetRenderFrameDef after "
- + std::to_string(t) + "ms.");
+ "GraphicsServer: timed out at " + std::to_string(t)
+ + "ms waiting for logic thread to send us a FrameDef.");
}
break; // Fail.
}
@@ -336,38 +332,6 @@ void GraphicsServer::LoadRenderer() {
texture_quality_ = Graphics::TextureQualityFromRequest(
texture_quality_requested_, renderer_->GetAutoTextureQuality());
- // If we don't support high quality graphics, make sure we're no higher than
- // medium.
- // BA_PRECONDITION(g_base->graphics->has_supports_high_quality_graphics_value());
- // if (!g_base->graphics->supports_high_quality_graphics()
- // && graphics_quality_ > GraphicsQuality::kMedium) {
- // graphics_quality_ = GraphicsQuality::kMedium;
- // }
- // graphics_quality_set_ = true;
-
- // Update texture quality based on request.
- // switch (texture_quality_requested_) {
- // case TextureQualityRequest::kLow:
- // texture_quality_ = TextureQuality::kLow;
- // break;
- // case TextureQualityRequest::kMedium:
- // texture_quality_ = TextureQuality::kMedium;
- // break;
- // case TextureQualityRequest::kHigh:
- // texture_quality_ = TextureQuality::kHigh;
- // break;
- // case TextureQualityRequest::kAuto:
- // texture_quality_ = renderer_->GetAutoTextureQuality();
- // break;
- // default:
- // Log(LogLevel::kError,
- // "Unhandled TextureQualityRequest value: "
- // +
- // std::to_string(static_cast(texture_quality_requested_)));
- // texture_quality_ = TextureQuality::kLow;
- // }
- // texture_quality_set_ = true;
-
// Ok we've got our qualities figured out; now load/update the renderer.
renderer_->Load();
@@ -657,4 +621,15 @@ auto GraphicsServer::InGraphicsContext_() const -> bool {
return g_base->app_adapter->InGraphicsContext();
}
+void GraphicsServer::Shutdown() {
+ BA_PRECONDITION(!shutting_down_);
+ BA_PRECONDITION(g_base->InGraphicsContext());
+ shutting_down_ = true;
+
+ // We don't actually do anything here currently; just take note
+ // that we're shutting down so we no longer wait for frames to come
+ // in from the main thread.
+ shutdown_completed_ = true;
+}
+
} // namespace ballistica::base
diff --git a/src/ballistica/base/graphics/graphics_server.h b/src/ballistica/base/graphics/graphics_server.h
index ed2cbd5b..dc3df09e 100644
--- a/src/ballistica/base/graphics/graphics_server.h
+++ b/src/ballistica/base/graphics/graphics_server.h
@@ -231,14 +231,6 @@ class GraphicsServer {
return tv_border_;
}
- // auto graphics_quality_set() const {
- // return graphics_quality_ != GraphicsQuality::kUnset;
- // }
-
- // auto texture_quality_set() const {
- // return texture_quality_ != TextureQuality::kUnset;
- // }
-
auto SupportsTextureCompressionType(TextureCompressionType t) const -> bool {
assert(InGraphicsContext_());
assert(texture_compression_types_set_);
@@ -249,10 +241,6 @@ class GraphicsServer {
void SetTextureCompressionTypes(
const std::list& types);
- // auto texture_compression_types_are_set() const {
- // return texture_compression_types_set_;
- // }
-
void set_renderer_context_lost(bool lost) { renderer_context_lost_ = lost; }
auto renderer_context_lost() const { return renderer_context_lost_; }
@@ -289,6 +277,11 @@ class GraphicsServer {
return texture_compression_types_;
}
+ /// Start spinning down the graphics server/etc.
+ void Shutdown();
+
+ auto shutdown_completed() const { return shutdown_completed_; }
+
private:
/// Pass a freshly allocated GraphicsContext instance, which the graphics
/// system will take ownership of.
@@ -324,6 +317,10 @@ class GraphicsServer {
}
}
+ TextureQualityRequest texture_quality_requested_{};
+ TextureQuality texture_quality_{};
+ GraphicsQualityRequest graphics_quality_requested_{};
+ GraphicsQuality graphics_quality_{};
bool renderer_loaded_ : 1 {};
bool model_view_projection_matrix_dirty_ : 1 {true};
bool model_world_matrix_dirty_ : 1 {true};
@@ -331,11 +328,9 @@ class GraphicsServer {
bool renderer_context_lost_ : 1 {};
bool texture_compression_types_set_ : 1 {};
bool cam_orient_matrix_dirty_ : 1 {true};
+ bool shutting_down_ : 1 {};
+ bool shutdown_completed_ : 1 {};
Snapshot* client_context_{};
- TextureQualityRequest texture_quality_requested_{};
- TextureQuality texture_quality_{};
- GraphicsQualityRequest graphics_quality_requested_{};
- GraphicsQuality graphics_quality_{};
float res_x_{};
float res_y_{};
float res_x_virtual_{};
diff --git a/src/ballistica/base/graphics/mesh/text_mesh.cc b/src/ballistica/base/graphics/mesh/text_mesh.cc
index f2c7e378..6b735175 100644
--- a/src/ballistica/base/graphics/mesh/text_mesh.cc
+++ b/src/ballistica/base/graphics/mesh/text_mesh.cc
@@ -360,7 +360,7 @@ void TextMesh::SetText(const std::string& text_in, HAlign alignment_h,
// compile it and add its final spans to our mesh.
if (packer) {
std::vector spans;
- packer->compile();
+ packer->Compile();
// DEBUGGING - add a single quad above our first
// span showing the entire texture for debugging purposes
diff --git a/src/ballistica/base/graphics/text/font_page_map_data.h b/src/ballistica/base/graphics/text/font_page_map_data.h
index 10ff01c3..60ea0aa8 100644
--- a/src/ballistica/base/graphics/text/font_page_map_data.h
+++ b/src/ballistica/base/graphics/text/font_page_map_data.h
@@ -12,25 +12,25 @@
#include "ballistica/base/graphics/text/text_graphics.h"
namespace ballistica::base {
-// the total number of glyph pages we have
+// The total number of glyph pages we have.
#define BA_GLYPH_PAGE_COUNT 8
-// the total number of glyphs we have
+// The total number of glyphs we have.
const int kGlyphCount = 1280;
-// the starting glyph index for each page
+// The starting glyph index for each page.
uint32_t g_glyph_page_start_index_map[8] = {0, 258, 416, 546,
698, 981, 1138, 1276};
-// the number of glyphs on each page
+// The number of glyphs on each page.
uint32_t g_glyph_page_glyph_counts[8] = {258, 158, 130, 152, 283, 157, 138, 4};
-// our dynamically-loaded glyph structs for each page
+// Our dynamically-loaded glyph structs for each page.
TextGraphics::Glyph* g_glyph_pages[8] = {nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr};
-// the page index for each glyph
-uint16_t g_glyph_map[kGlyphCount] = {
+// The page index for each glyph.
+uint8_t g_glyph_map[kGlyphCount] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
diff --git a/src/ballistica/base/graphics/text/text_graphics.cc b/src/ballistica/base/graphics/text/text_graphics.cc
index 5a5cd8de..fae81787 100644
--- a/src/ballistica/base/graphics/text/text_graphics.cc
+++ b/src/ballistica/base/graphics/text/text_graphics.cc
@@ -116,8 +116,7 @@ TextGraphics::TextGraphics() {
}
}
- // init glyph values for our big font page
- // (a 8x8 array)
+ // Init glyph values for our big font page (a 8x8 array).
{
float x_offs = 0.009f;
float y_offs = 0.0059f;
@@ -284,7 +283,7 @@ TextGraphics::TextGraphics() {
(1.0f / 8.0f) * static_cast(x + 1) + x_offs + scale_extra;
g.tex_max_y = (1.0f / 8.0f) * static_cast(y) + y_offs;
- // just scooted letters over.. account for that
+ // Just scooted letters over; account for that.
float foo_x = 0.0183f;
float foo_y = 0.000f;
g.tex_min_x += foo_x;
@@ -292,12 +291,12 @@ TextGraphics::TextGraphics() {
g.tex_min_y += foo_y;
g.tex_max_y += foo_y;
- // clamp based on char width
+ // Clamp based on char width.
float scale = w * 1.32f;
g.x_size *= scale;
g.tex_max_x = g.tex_min_x + (g.tex_max_x - g.tex_min_x) * scale;
- // add bot offset
+ // Add bot offset.
if (bot_offset != 0.0f) {
g.tex_min_y = g.tex_max_y
+ (g.tex_min_y - g.tex_max_y)
@@ -305,7 +304,7 @@ TextGraphics::TextGraphics() {
g.pen_offset_y -= bot_offset;
g.y_size += bot_offset;
}
- // add left offset
+ // Add left offset.
if (left_offset != 0.0f) {
g.tex_min_x = g.tex_max_x
+ (g.tex_min_x - g.tex_max_x)
@@ -313,14 +312,14 @@ TextGraphics::TextGraphics() {
g.pen_offset_x -= left_offset;
g.x_size += left_offset;
}
- // add right offset
+ // Add right offset.
if (right_offset != 0.0f) {
g.tex_max_x = g.tex_min_x
+ (g.tex_max_x - g.tex_min_x)
* ((g.x_size + right_offset) / g.x_size);
g.x_size += right_offset;
}
- // add top offset
+ // Add top offset.
if (top_offset != 0.0f) {
g.tex_max_y = g.tex_min_y
+ (g.tex_max_y - g.tex_min_y)
@@ -844,13 +843,13 @@ void TextGraphics::GetFontPageCharRange(int page, uint32_t* first_char,
// Our special pages:
switch (page) {
case static_cast(FontPage::kOSRendered): {
- // we allow the OS to render anything not in one of our glyph textures
- // (technically this overlaps the private-use range which we use our own
- // textures for, but that's handled as a special-case by
- // TextGroup::setText
+ // We allow the OS to render anything not in one of our glyph textures
+ // (technically this overlaps the private-use range which we use our
+ // own textures for, but that's handled as a special-case by
+ // TextGroup::SetText.
(*first_char) = kGlyphCount;
- (*last_char) = kTextMaxUnicodeVal; // hmm what's the max unicode value we
- // should ever see?..
+ // hmm what's the max unicode value we should ever see?..
+ (*last_char) = kTextMaxUnicodeVal;
break;
}
case static_cast(FontPage::kExtras1): {
@@ -887,52 +886,57 @@ void TextGraphics::GetFontPagesForText(const std::string& text,
int last_page = -1;
std::vector unicode = Utils::UnicodeFromUTF8(text, "c03853");
for (uint32_t val : unicode) {
- int page;
+ int page{-1};
- // Hack: allow showing euro even if we don't support unicode font rendering.
- if (g_buildconfig.enable_os_font_rendering()) {
- if (val == 8364) {
- val = 0xE000;
- }
- }
+ // Hack: allow showing euro even if we don't support unicode font
+ // rendering.
+ // if (g_buildconfig.enable_os_font_rendering()) {
+ // if (val == 8364) {
+ // val = 0xE000;
+ // }
+ // }
- // For values in the custom-char range (U+E000–U+F8FF) we point at our own
- // custom page(s)
+ bool covered{};
+
+ // For values in the custom-char range (U+E000–U+F8FF) we point at our
+ // own custom page(s)
if (val >= 0xE000 && val <= 0xF8FF) {
// The 25 chars after this are in our fontExtras sheet.
if (val < 0xE000 + 25) {
// Special value denoting our custom font page.
page = static_cast(FontPage::kExtras1);
+ covered = true;
} else if (val < 0xE000 + 50) {
// Special value denoting our custom font page.
page = static_cast(FontPage::kExtras2);
+ covered = true;
} else if (val < 0xE000 + 75) {
// Special value denoting our custom font page.
page = static_cast(FontPage::kExtras3);
+ covered = true;
} else if (val < 0xE000 + 100) {
// Special value denoting our custom font page.
page = static_cast(FontPage::kExtras4);
- } else {
- // We dont cover this.. just go with '?'
- val = '?';
- page = g_glyph_map[val];
+ covered = true;
}
- } else if (val >= kGlyphCount) {
- // Otherwise if its outside of our texture-coverage area.
+ } else if (val < kGlyphCount) {
+ page = g_glyph_map[val];
+ covered = true;
+ }
+
+ if (!covered) {
if (g_buildconfig.enable_os_font_rendering()) {
page = static_cast(FontPage::kOSRendered);
} else {
val = '?';
page = g_glyph_map[val];
}
- } else {
- // yay we cover it!
- page = g_glyph_map[val];
}
- // compare to lastPage to avoid doing a set insert for *everything* since
- // most will be the same
+
+ // Compare to last_page to avoid doing a set insert for *everything*
+ // since most will be the same.
if (page != last_page) {
- (*font_pages).insert(page);
+ font_pages->insert(page);
last_page = page;
}
}
@@ -1009,12 +1013,8 @@ void TextGraphics::GetOSTextSpanBoundsAndWidth(const std::string& s, Rect* r,
// Send this entry to the back of the list since we used it.
text_span_bounds_cache_.erase(entry->list_iterator_);
- // I guess inspection doesn't realize entry lives on after this?...
-#pragma clang diagnostic push
-#pragma ide diagnostic ignored "UnusedValue"
entry->list_iterator_ =
text_span_bounds_cache_.insert(text_span_bounds_cache_.end(), entry);
-#pragma clang diagnostic pop
return;
}
auto entry(Object::New());
@@ -1068,7 +1068,9 @@ auto TextGraphics::GetStringWidth(const char* text, bool big) -> float {
line_length += GetOSTextSpanWidth(s);
os_span.clear();
}
- if (line_length > max_line_length) max_line_length = line_length;
+ if (line_length > max_line_length) {
+ max_line_length = line_length;
+ }
line_length = 0;
t++;
} else {
@@ -1145,7 +1147,9 @@ void TextGraphics::BreakUpString(const char* text, float width,
s_begin = t;
}
} else {
- if (*t == 0) throw Exception();
+ if (*t == 0) {
+ throw Exception();
+ }
uint32_t val = Utils::GetUTF8Value(t);
Utils::AdvanceUTF8(&t);
diff --git a/src/ballistica/base/graphics/text/text_packer.cc b/src/ballistica/base/graphics/text/text_packer.cc
index 2f9ea250..fffd74b1 100644
--- a/src/ballistica/base/graphics/text/text_packer.cc
+++ b/src/ballistica/base/graphics/text/text_packer.cc
@@ -24,11 +24,11 @@ void TextPacker::AddSpan(const std::string& text, float x, float y,
}
// FIXME - we currently run into minor problems because we measure our text
-// bounds at one size and then scale that linearly when trying to fit things
-// into the texture. However, fonts don't always scale linearly (and even when
-// that's an option it can be expensive).
+// bounds at one size and then scale that linearly when trying to fit
+// things into the texture. However, fonts don't always scale linearly (and
+// even when that's an option it can be expensive).
-void TextPacker::compile() {
+void TextPacker::Compile() {
assert(!compiled_);
if (spans_.empty()) {
compiled_ = true;
@@ -57,24 +57,23 @@ void TextPacker::compile() {
width *= 2;
}
- // Alternately, if we're too big, crank our scale down so that our widest span
- // fits.
+ // Alternately, if we're too big, crank our scale down so that our widest
+ // span fits.
if (widest_unscaled_span_width * scale > width * 0.9f) {
scale *= ((width * 0.9f) / (widest_unscaled_span_width * scale));
}
float start_height = height;
int mini_shrink_tries = 0;
- // Ok; we've now locked in a width and scale.
- // Now we go through and position our spans.
- // We may need to do this more than once if our height comes out too big.
- // (hopefully this will never be a problem in practice)
+ // Ok; we've now locked in a width and scale. Now we go through and
+ // position our spans. We may need to do this more than once if our height
+ // comes out too big. (hopefully this will never be a problem in practice)
while (true) {
height = start_height;
- // We currently just lay out left-to-right, top-to-bottom.
- // This could be somewhat wasteful in particular configurations.
- // (leaving half-filled lines, etc) so it might be worth improving later.
+ // We currently just lay out left-to-right, top-to-bottom. This could be
+ // somewhat wasteful in particular configurations. (leaving half-filled
+ // lines, etc) so it might be worth improving later.
float widest_fill_right = 0.0f;
float fill_right = 0.0f;
float fill_bottom = 0.0f;
@@ -87,26 +86,27 @@ void TextPacker::compile() {
// Start a new line if this would put us past the end.
if (fill_right + span_width > width) {
if (fill_right > widest_fill_right) {
- widest_fill_right = fill_right; // Keep track of how far over we go.
+ // Keep track of how far over we go.
+ widest_fill_right = fill_right;
}
fill_right = 0.0f;
fill_bottom += line_height;
line_height = 0.0f;
}
- // Position x such that x + left bound - buffer lines up with our current
- // right point.
+ // Position x such that x + left bound - buffer lines up with our
+ // current right point.
float to_left = (i.bounds.l - span_buffer) * scale;
i.tex_x = fill_right - to_left;
fill_right += span_width;
- // Position y such that y - top bound - buffer lines up with our current
- // bottom point.
+ // Position y such that y - top bound - buffer lines up with our
+ // current bottom point.
float to_top = (-i.bounds.t - span_buffer) * scale;
i.tex_y = fill_bottom - to_top;
- // If our total height is greater than the current line height, expand the
- // line's.
+ // If our total height is greater than the current line height, expand
+ // the line's.
if (span_height > line_height) {
line_height = span_height;
}
@@ -125,9 +125,9 @@ void TextPacker::compile() {
// If it doesn't fit, repeat again with a smaller scale until it does.
// Dropping our scale has a disproportional effect on the final height
- // (since it opens up more relative horizontal space).
- // I'm not sure how to figure out how much to drop by other than
- // incrementally dropping values until we fit.
+ // (since it opens up more relative horizontal space). I'm not sure
+ // how to figure out how much to drop by other than incrementally
+ // dropping values until we fit.
scale *= 0.75f;
} else if (((widest_fill_right < (width * mini_shrink_threshold_h)
@@ -135,15 +135,15 @@ void TextPacker::compile() {
|| fill_bottom + line_height
< (height * mini_shrink_threshold_v))
&& mini_shrink_tries < 3) {
- // If we're here it means we *barely* use more than half of the texture in
- // one direction or the other; let's shrink just a tiny bit and we should
- // be able to chop our texture size in half
+ // If we're here it means we *barely* use more than half of the
+ // texture in one direction or the other; let's shrink just a tiny bit
+ // and we should be able to chop our texture size in half
if (widest_fill_right < width * mini_shrink_threshold_h && width > 16) {
float scale_val = 0.99f * (((width * 0.5f) / widest_fill_right));
if (scale_val < 1.0f) {
- // FIXME - should think about a fixed multiplier here;
- // under the hood the system might be caching glyphs based on scale
- // and this would leave us with fewer different scales in the end and
+ // FIXME - should think about a fixed multiplier here; under the
+ // hood the system might be caching glyphs based on scale and
+ // this would leave us with fewer different scales in the end and
// thus better caching performance
scale *= scale_val;
}
@@ -151,9 +151,9 @@ void TextPacker::compile() {
} else {
float scale_val = 0.99f * (height * 0.5f) / (fill_bottom + line_height);
if (scale_val < 1.0f) {
- // FIXME - should think about a fixed multiplier here;
- // under the hood the system might be caching glyphs based on scale
- // and this would leave us with fewer different scales in the end and
+ // FIXME - should think about a fixed multiplier here; under the
+ // hood the system might be caching glyphs based on scale and
+ // this would leave us with fewer different scales in the end and
// thus better caching performance
scale *= scale_val;
}
@@ -165,8 +165,8 @@ void TextPacker::compile() {
}
}
- // Lastly, now that our texture width and height are completely finalized, we
- // can calculate UVs.
+ // Lastly, now that our texture width and height are completely finalized,
+ // we can calculate UVs.
for (auto&& i : spans_) {
// Now store uv coords for this span; they should include the buffer.
i.u_min = (i.tex_x + (i.bounds.l - span_buffer) * scale) / width;
@@ -182,11 +182,11 @@ void TextPacker::compile() {
}
// TODO(ericf): now we calculate a hash that's unique to this text
- // configuration; we'll use that as a key for the texture we'll generate/use.
- // ..this way multiple meshes can share the same generated texture.
- // *technically* we could calculate this hash and check for an existing
- // texture before we bother laying out our spans, but that might not save us
- // much time and would complicate things.
+ // configuration; we'll use that as a key for the texture we'll
+ // generate/use. ..this way multiple meshes can share the same generated
+ // texture. *technically* we could calculate this hash and check for an
+ // existing texture before we bother laying out our spans, but that might
+ // not save us much time and would complicate things.
hash_ = std::to_string(resolution_scale_);
for (auto&& i : spans_) {
char buffer[64];
diff --git a/src/ballistica/base/graphics/text/text_packer.h b/src/ballistica/base/graphics/text/text_packer.h
index d1889ba8..e2c40310 100644
--- a/src/ballistica/base/graphics/text/text_packer.h
+++ b/src/ballistica/base/graphics/text/text_packer.h
@@ -21,7 +21,7 @@ class TextPacker : public Object {
// outside of here anyway so might as well recycle.
void AddSpan(const std::string& text, float x, float y, const Rect& bounds);
- auto hash() const -> const std::string& {
+ const auto& hash() const {
assert(compiled_);
return hash_;
}
@@ -51,32 +51,32 @@ class TextPacker : public Object {
// Once done adding spans, call this to calculate final span UV values,
// texture configuration, and hash.
- void compile();
+ void Compile();
- auto spans() const -> const std::list& { return spans_; }
+ const auto& spans() const { return spans_; }
- auto texture_width() const -> int {
+ auto texture_width() const {
assert(compiled_);
return texture_width_;
}
- auto texture_height() const -> int {
+ auto texture_height() const {
assert(compiled_);
return texture_height_;
}
- auto text_scale() const -> float {
+ auto text_scale() const {
assert(compiled_);
return text_scale_;
}
private:
+ bool compiled_{false};
float resolution_scale_;
+ float text_scale_{};
int texture_width_{};
int texture_height_{};
- float text_scale_{};
std::string hash_;
- bool compiled_{false};
std::list spans_;
};
diff --git a/src/ballistica/base/input/device/joystick_input.cc b/src/ballistica/base/input/device/joystick_input.cc
index 7d6df160..08ad4148 100644
--- a/src/ballistica/base/input/device/joystick_input.cc
+++ b/src/ballistica/base/input/device/joystick_input.cc
@@ -9,6 +9,7 @@
#include "ballistica/base/input/input.h"
#include "ballistica/base/python/base_python.h"
#include "ballistica/base/support/classic_soft.h"
+#include "ballistica/base/support/repeater.h"
#include "ballistica/base/ui/ui.h"
#include "ballistica/core/core.h"
#include "ballistica/shared/foundation/event_loop.h"
@@ -17,10 +18,6 @@
namespace ballistica::base {
-const char* kMFiControllerName = "iOS/Mac Controller";
-
-const int kJoystickRepeatDelay{500};
-
// Joy values below this are candidates for calibration.
const float kJoystickCalibrationThreshold{6000.0f};
@@ -78,20 +75,6 @@ JoystickInput::JoystickInput(int sdl_joystick_id,
&& raw_sdl_joystick_name_.size() <= 22) {
raw_sdl_joystick_name_ = "XInput Controller";
}
- // #else
- // raw_sdl_joystick_name_ = SDL_JoystickName(sdl_joystick_id_);
- // #endif // BA_SDL2_BUILD
-
- // If its an SDL joystick and we're using our custom sdl 1.2 build, ask it.
- // #if BA_XCODE_BUILD && BA_OSTYPE_MACOS && !BA_SDL2_BUILD
- // raw_sdl_joystick_identifier_ =
- // SDL_JoystickIdentifier(sdl_joystick_id_);
- // #endif
-
- // Some special-cases on mac.
- if (strstr(raw_sdl_joystick_name_.c_str(), "PLAYSTATION") != nullptr) {
- is_mac_ps3_controller_ = true;
- }
#else // BA_ENABLE_SDL_JOYSTICKS
throw Exception(); // Shouldn't happen.
@@ -101,8 +84,6 @@ JoystickInput::JoystickInput(int sdl_joystick_id,
// Its a manual joystick.
sdl_joystick_ = nullptr;
- is_mfi_controller_ = (custom_device_name_ == kMFiControllerName);
-
// Hard code a few remote controls.
// The newer way to do this is just set 'UI-Only' on the device config
is_remote_control_ = ((custom_device_name_ == "Amazon Remote")
@@ -179,6 +160,7 @@ auto JoystickInput::GetButtonName(int index) -> std::string {
break;
}
}
+
if (g_buildconfig.ostype_android()) {
// Special case: if this is a samsung controller, return the dice
// button icons.
@@ -446,46 +428,6 @@ void JoystickInput::Update() {
}
}
}
-
- // If a button's being held, potentially pass repeats along.
- if (up_held_ || down_held_ || left_held_ || right_held_) {
- // Don't ask for the widget unless we have something held.
- // (otherwise we prevent other inputs from getting at it)
- if (g_base->ui->GetWidgetForInput(this)) {
- millisecs_t repeat_delay = kJoystickRepeatDelay;
-
- millisecs_t t = g_core->GetAppTimeMillisecs();
- auto c = WidgetMessage::Type::kEmptyMessage;
- if (t - last_hold_time_ < repeat_delay) {
- return;
- }
-
- if (t - last_hold_time_ >= repeat_delay) {
- bool pass = false;
- if (up_held_) {
- pass = true;
- c = WidgetMessage::Type::kMoveUp;
- } else if (down_held_) {
- pass = true;
- c = WidgetMessage::Type::kMoveDown;
- } else if (left_held_) {
- pass = true;
- c = WidgetMessage::Type::kMoveLeft;
- } else if (right_held_) {
- pass = true;
- c = WidgetMessage::Type::kMoveRight;
- }
- if (pass) {
- g_base->ui->SendWidgetMessage(WidgetMessage(c));
- }
-
- // Set another repeat to happen sooner.
- last_hold_time_ =
- t
- - static_cast(static_cast(repeat_delay) * 0.8f);
- }
- }
- }
}
void JoystickInput::SetStandardExtendedButtons() {
@@ -508,6 +450,8 @@ void JoystickInput::ResetHeldStates() {
SDL_Event e;
dpad_right_held_ = dpad_left_held_ = dpad_up_held_ = dpad_down_held_ = false;
+ ui_repeater_.Clear();
+
run_buttons_held_.clear();
run_trigger1_value_ = run_trigger2_value_ = 0.0f;
UpdateRunningState();
@@ -594,28 +538,28 @@ void JoystickInput::HandleSDLEvent(const SDL_Event* e) {
|| dpad_down_held_))
return;
- bool isHoldPositionEvent = false;
+ bool is_hold_position_event = false;
// Keep track of whether hold-position is being held. If so, we don't send
- // window events. (some joysticks always give us significant axis values but
+ // window events (some joysticks always give us significant axis values but
// rely on hold position to keep from doing stuff usually).
if (e->type == SDL_JOYBUTTONDOWN
&& e->jbutton.button == hold_position_button_) {
need_to_send_held_state_ = true;
hold_position_held_ = true;
- isHoldPositionEvent = true;
+ is_hold_position_event = true;
}
if (e->type == SDL_JOYBUTTONUP
&& e->jbutton.button == hold_position_button_) {
need_to_send_held_state_ = true;
hold_position_held_ = false;
- isHoldPositionEvent = true;
+ is_hold_position_event = true;
}
// Let's ignore events for just a moment after we're created.
// (some joysticks seem to spit out erroneous button-pressed events when
// first plugged in ).
- if (time - creation_time_ < 250 && !isHoldPositionEvent) {
+ if (time - creation_time_ < 250 && !is_hold_position_event) {
return;
}
@@ -700,7 +644,7 @@ void JoystickInput::HandleSDLEvent(const SDL_Event* e) {
}
}
- // If its the ignore button, ignore it.
+ // If its an ignored button, ignore it.
if ((e->type == SDL_JOYBUTTONDOWN || e->type == SDL_JOYBUTTONUP)
&& (e->jbutton.button == ignored_button_
|| e->jbutton.button == ignored_button2_
@@ -709,49 +653,6 @@ void JoystickInput::HandleSDLEvent(const SDL_Event* e) {
return;
}
- // A little pre-filtering on mac PS3 gamepads. (try to filter out some noise
- // we're seeing, etc).
- if (g_buildconfig.ostype_macos() && is_mac_ps3_controller_) {
- switch (e->type) {
- case SDL_JOYAXISMOTION: {
- // On my ps3 controller, I seem to be seeing occasional joy-axis-events
- // coming in with values of -32768 when nothing is being touched.
- // Filtering those out here.. Should look into this more and see if its
- // SDL's fault or else forward a bug to apple.
- if ((e->jaxis.axis == 0 || e->jaxis.axis == 1)
- && e->jaxis.value == -32768
- && (time - ps3_last_joy_press_time_ > 2000) && !ps3_jaxis1_pressed_
- && !ps3_jaxis2_pressed_) {
- printf(
- "BAJoyStick notice: filtering out errand PS3 axis %d value of "
- "%d\n",
- static_cast(e->jaxis.axis),
- static_cast(e->jaxis.value));
- fflush(stdout);
-
- // std::cout << "BSJoyStick notice: filtering out errant PS3 axis " <<
- // int(e->jaxis.axis) << " value of " << e->jaxis.value << std::endl;
- return;
- }
-
- if (abs(e->jaxis.value) >= kJoystickDiscreteThreshold) {
- ps3_last_joy_press_time_ = time;
- }
-
- // Keep track of whether its pressed for next time.
- if (e->jaxis.axis == 0) {
- ps3_jaxis1_pressed_ = (abs(e->jaxis.value) > 3000);
- } else if (e->jaxis.axis == 1) {
- ps3_jaxis2_pressed_ = (abs(e->jaxis.value) > 3000);
- }
-
- break;
- }
- default:
- break;
- }
- }
-
// A few high level button press interceptions.
if (e->type == SDL_JOYBUTTONDOWN) {
if (e->jbutton.button == start_button_
@@ -806,111 +707,116 @@ void JoystickInput::HandleSDLEvent(const SDL_Event* e) {
}
}
- // If we're in a dialog, send dialog events.
- // We keep track of special x/y values for dialog usage.
+ // If we're in the ui, send ui events.
+ // We keep track of special x/y values for ui usage.
// These are formed as combinations of the actual joy value
// and the hold-position state.
// Think of hold-position as somewhat of a 'magnitude' to the joy event's
// direction. They're really one and the same event. (we just need to store
// their states ourselves since they don't both come through at once).
- bool isAnalogStickJAxisEvent = false;
+ // FIXME: Ugh need to rip out this old hold-position stuff.
+ bool is_analog_stick_jaxis_event = false;
if (e->type == SDL_JOYAXISMOTION) {
if (e->jaxis.axis == analog_lr_) {
dialog_jaxis_x_ = e->jaxis.value;
- isAnalogStickJAxisEvent = true;
+ is_analog_stick_jaxis_event = true;
} else if (e->jaxis.axis == analog_ud_) {
dialog_jaxis_y_ = e->jaxis.value;
- isAnalogStickJAxisEvent = true;
+ is_analog_stick_jaxis_event = true;
}
}
- int dialogJaxisX = dialog_jaxis_x_;
+ int ui_jaxis_x = dialog_jaxis_x_;
if (hold_position_held_) {
- dialogJaxisX = 0; // Throttle is off.
+ ui_jaxis_x = 0; // Throttle is off.
}
- int dialogJaxisY = dialog_jaxis_y_;
+ int ui_jaxis_y = dialog_jaxis_y_;
if (hold_position_held_) {
- dialogJaxisY = 0; // Throttle is off.
+ ui_jaxis_y = 0; // Throttle is off.
}
// We might not wanna grab at the UI if we're a axis-motion event
// below our 'pressed' threshold.. Otherwise fuzzy analog joystick
// readings would cause rampant UI stealing even if no events are being sent.
- bool would_go_to_dialog = false;
+ bool would_go_to_ui = false;
auto wm = WidgetMessage::Type::kEmptyMessage;
- if (isAnalogStickJAxisEvent || isHoldPositionEvent) {
+ if (is_analog_stick_jaxis_event || is_hold_position_event) {
// Even when we're not sending, clear out some 'held' states.
- if (left_held_ && dialogJaxisX >= -kJoystickDiscreteThreshold) {
+ if (left_held_ && ui_jaxis_x >= -kJoystickDiscreteThreshold) {
left_held_ = false;
+ ui_repeater_.Clear();
}
- if (right_held_ && dialogJaxisX <= kJoystickDiscreteThreshold) {
+ if (right_held_ && ui_jaxis_x <= kJoystickDiscreteThreshold) {
right_held_ = false;
+ ui_repeater_.Clear();
}
- if (up_held_ && dialogJaxisY >= -kJoystickDiscreteThreshold) {
+ if (up_held_ && ui_jaxis_y >= -kJoystickDiscreteThreshold) {
up_held_ = false;
+ ui_repeater_.Clear();
}
- if (down_held_ && dialogJaxisY <= kJoystickDiscreteThreshold) {
+ if (down_held_ && ui_jaxis_y <= kJoystickDiscreteThreshold) {
down_held_ = false;
+ ui_repeater_.Clear();
+ }
+ if ((!right_held_) && ui_jaxis_x > kJoystickDiscreteThreshold) {
+ would_go_to_ui = true;
+ }
+ if ((!left_held_) && ui_jaxis_x < -kJoystickDiscreteThreshold) {
+ would_go_to_ui = true;
+ }
+ if ((!up_held_) && ui_jaxis_y < -kJoystickDiscreteThreshold) {
+ would_go_to_ui = true;
+ }
+ if ((!down_held_) && ui_jaxis_y > kJoystickDiscreteThreshold) {
+ would_go_to_ui = true;
}
- if ((!right_held_) && dialogJaxisX > kJoystickDiscreteThreshold)
- would_go_to_dialog = true;
- if ((!left_held_) && dialogJaxisX < -kJoystickDiscreteThreshold)
- would_go_to_dialog = true;
- if ((!up_held_) && dialogJaxisY < -kJoystickDiscreteThreshold)
- would_go_to_dialog = true;
- if ((!down_held_) && dialogJaxisY > kJoystickDiscreteThreshold)
- would_go_to_dialog = true;
} else if ((e->type == SDL_JOYHATMOTION && e->jhat.hat == hat_)
|| (e->type == SDL_JOYBUTTONDOWN
&& e->jbutton.button != hold_position_button_)) {
// Other button-downs and hat motions always go.
- would_go_to_dialog = true;
+ would_go_to_ui = true;
}
// Resets always circumvent dialogs.
- if (resetting_) would_go_to_dialog = false;
+ if (resetting_) {
+ would_go_to_ui = false;
+ }
- // Anything that would go to a dialog also counts to mark us as
- // 'recently-used'.
- if (would_go_to_dialog) {
+ // Anything that would go to ui also counts to mark us as 'recently-used'.
+ if (would_go_to_ui) {
UpdateLastInputTime();
}
- if (would_go_to_dialog && g_base->ui->GetWidgetForInput(this)) {
- bool pass = false;
+ if (would_go_to_ui && g_base->ui->GetWidgetForInput(this)) {
+ bool pass{};
// Special case.. either joy-axis-motion or hold-position events trigger
// these.
- if (isAnalogStickJAxisEvent || isHoldPositionEvent) {
- if (dialogJaxisX > kJoystickDiscreteThreshold) {
- // To the right.
+ if (is_analog_stick_jaxis_event || is_hold_position_event) {
+ if (ui_jaxis_x > kJoystickDiscreteThreshold) {
if (!right_held_ && !up_held_ && !down_held_) {
- last_hold_time_ = g_core->GetAppTimeMillisecs();
right_held_ = true;
+ pass = true;
wm = WidgetMessage::Type::kMoveRight;
- pass = true;
}
- } else if (dialogJaxisX < -kJoystickDiscreteThreshold) {
+ } else if (ui_jaxis_x < -kJoystickDiscreteThreshold) {
if (!left_held_ && !up_held_ && !down_held_) {
- last_hold_time_ = g_core->GetAppTimeMillisecs();
- wm = WidgetMessage::Type::kMoveLeft;
- pass = true;
left_held_ = true;
+ pass = true;
+ wm = WidgetMessage::Type::kMoveLeft;
}
}
- if (dialogJaxisY > kJoystickDiscreteThreshold) {
+ if (ui_jaxis_y > kJoystickDiscreteThreshold) {
if (!down_held_ && !left_held_ && !right_held_) {
- last_hold_time_ = g_core->GetAppTimeMillisecs();
- wm = WidgetMessage::Type::kMoveDown;
- pass = true;
down_held_ = true;
- }
- } else if (dialogJaxisY < -kJoystickDiscreteThreshold) {
- if (!up_held_ && !left_held_ && !right_held_) {
- last_hold_time_ = g_core->GetAppTimeMillisecs();
- wm = WidgetMessage::Type::kMoveUp;
pass = true;
+ wm = WidgetMessage::Type::kMoveDown;
+ }
+ } else if (ui_jaxis_y < -kJoystickDiscreteThreshold) {
+ if (!up_held_ && !left_held_ && !right_held_) {
up_held_ = true;
+ pass = true;
+ wm = WidgetMessage::Type::kMoveUp;
}
}
}
@@ -924,7 +830,6 @@ void JoystickInput::HandleSDLEvent(const SDL_Event* e) {
switch (e->jhat.value) {
case SDL_HAT_LEFT: {
if (!left_held_) {
- last_hold_time_ = g_core->GetAppTimeMillisecs();
wm = WidgetMessage::Type::kMoveLeft;
pass = true;
left_held_ = true;
@@ -935,7 +840,6 @@ void JoystickInput::HandleSDLEvent(const SDL_Event* e) {
case SDL_HAT_RIGHT: {
if (!right_held_) {
- last_hold_time_ = g_core->GetAppTimeMillisecs();
wm = WidgetMessage::Type::kMoveRight;
pass = true;
right_held_ = true;
@@ -945,7 +849,6 @@ void JoystickInput::HandleSDLEvent(const SDL_Event* e) {
}
case SDL_HAT_UP: {
if (!up_held_) {
- last_hold_time_ = g_core->GetAppTimeMillisecs();
wm = WidgetMessage::Type::kMoveUp;
pass = true;
up_held_ = true;
@@ -955,7 +858,6 @@ void JoystickInput::HandleSDLEvent(const SDL_Event* e) {
}
case SDL_HAT_DOWN: {
if (!down_held_) {
- last_hold_time_ = g_core->GetAppTimeMillisecs();
wm = WidgetMessage::Type::kMoveDown;
pass = true;
down_held_ = true;
@@ -968,6 +870,7 @@ void JoystickInput::HandleSDLEvent(const SDL_Event* e) {
down_held_ = false;
left_held_ = false;
right_held_ = false;
+ ui_repeater_.Clear();
}
default:
break;
@@ -1007,7 +910,20 @@ void JoystickInput::HandleSDLEvent(const SDL_Event* e) {
break;
}
if (pass) {
- g_base->ui->SendWidgetMessage(WidgetMessage(wm));
+ switch (wm) {
+ case WidgetMessage::Type::kMoveUp:
+ case WidgetMessage::Type::kMoveDown:
+ case WidgetMessage::Type::kMoveLeft:
+ case WidgetMessage::Type::kMoveRight:
+ // For UI movement, set up a repeater so we can hold the button.
+ ui_repeater_ = Repeater::New(
+ kUINavigationRepeatDelay, kUINavigationRepeatInterval,
+ [wm] { g_base->ui->SendWidgetMessage(WidgetMessage(wm)); });
+ break;
+ default:
+ // Other messages are just one-shots.
+ g_base->ui->SendWidgetMessage(WidgetMessage(wm));
+ }
}
return;
}
@@ -1502,22 +1418,7 @@ auto JoystickInput::GetRawDeviceName() -> std::string {
auto JoystickInput::GetDeviceExtraDescription() -> std::string {
std::string s;
- // On mac, PS3 controllers can connect via USB or bluetooth,
- // and it can be confusing if one is doing both,
- // so lets specify here.
- if (GetDeviceName() == "PLAYSTATION(R)3 Controller") {
- // For bluetooth we get a serial in the form "04-76-6e-d1-17-90" while
- // on USB we get a simple int (the usb location id): "-9340234"
- // so lets consider it wireless if its got a dash not at the beginning.
- s = " (USB)";
-
- auto dname = GetDeviceIdentifier();
- for (const char* tst = dname.c_str(); *tst; tst++) {
- if (*tst == '-' && tst != dname) {
- s = " (Bluetooth)";
- }
- }
- }
+ // (no longer being used).
return s;
}
diff --git a/src/ballistica/base/input/device/joystick_input.h b/src/ballistica/base/input/device/joystick_input.h
index e6133154..5748f702 100644
--- a/src/ballistica/base/input/device/joystick_input.h
+++ b/src/ballistica/base/input/device/joystick_input.h
@@ -16,7 +16,7 @@ namespace ballistica::base {
const int kJoystickDiscreteThreshold{15000};
const float kJoystickDiscreteThresholdFloat{0.46f};
const int kJoystickAnalogCalibrationDivisions{20};
-extern const char* kMFiControllerName;
+// extern const char* kMFiControllerName;
/// A physical game controller.
class JoystickInput : public InputDevice {
@@ -87,48 +87,48 @@ class JoystickInput : public InputDevice {
void UpdateRunningState();
auto GetCalibratedValue(float raw, float neutral) const -> int32_t;
- std::string raw_sdl_joystick_name_;
- std::string raw_sdl_joystick_identifier_;
- float run_value_{};
JoystickInput* child_joy_stick_{};
JoystickInput* parent_joy_stick_{};
millisecs_t last_ui_only_print_time_{};
- bool ui_only_{};
- bool unassigned_buttons_run_{true};
- bool start_button_activates_default_widget_{true};
- bool auto_recalibrate_analog_stick_{};
millisecs_t creation_time_{};
- bool did_initial_reset_{};
// FIXME - should take this out and replace it with a bool
// (we never actually access the sdl joystick directly outside of our
// constructor)
SDL_Joystick* sdl_joystick_{};
- bool is_test_input_{};
- bool is_remote_control_{};
- bool is_remote_app_{};
- bool is_mfi_controller_{};
- bool is_mac_ps3_controller_{};
-
- millisecs_t ps3_last_joy_press_time_{-10000};
+ bool ui_only_ : 1 {};
+ bool unassigned_buttons_run_ : 1 {true};
+ bool start_button_activates_default_widget_ : 1 {true};
+ bool auto_recalibrate_analog_stick_ : 1 {};
+ bool did_initial_reset_ : 1 {};
+ bool is_test_input_ : 1 {};
+ bool is_remote_control_ : 1 {};
+ bool is_remote_app_ : 1 {};
+ bool is_mfi_controller_ : 1 {};
// For dialogs.
- bool left_held_{};
- bool right_held_{};
- bool up_held_{};
- bool down_held_{};
- bool hold_position_held_{};
- bool need_to_send_held_state_{};
+ bool left_held_ : 1 {};
+ bool right_held_ : 1 {};
+ bool up_held_ : 1 {};
+ bool down_held_ : 1 {};
+ bool hold_position_held_ : 1 {};
+ bool need_to_send_held_state_ : 1 {};
+
+ bool hat_held_ : 1 {};
+ bool dpad_right_held_ : 1 {};
+ bool dpad_left_held_ : 1 {};
+ bool dpad_up_held_ : 1 {};
+ bool dpad_down_held_ : 1 {};
+
+ bool ignore_completely_ : 1 {};
+ bool resetting_ : 1 {};
+ bool calibrate_ : 1 {};
+ bool can_configure_ : 1 {};
+
int hat_{};
int analog_lr_{};
int analog_ud_{1};
- millisecs_t last_hold_time_{};
- bool hat_held_{};
- bool dpad_right_held_{};
- bool dpad_left_held_{};
- bool dpad_up_held_{};
- bool dpad_down_held_{};
// Mappings of ba buttons to SDL buttons.
int jump_button_{};
@@ -143,7 +143,6 @@ class JoystickInput : public InputDevice {
// Used on rift build; we have one button which we disallow from joining but
// the rest we allow. (all devices are treated as one and the same there).
int remote_enter_button_{-1};
- bool ignore_completely_{};
int ignored_button_{-1};
int ignored_button2_{-1};
int ignored_button3_{-1};
@@ -153,12 +152,6 @@ class JoystickInput : public InputDevice {
int run_trigger1_{-1};
int run_trigger2_{-1};
int vr_reorient_button_{-1};
- float run_trigger1_min_{};
- float run_trigger1_max_{};
- float run_trigger2_min_{};
- float run_trigger2_max_{};
- float run_trigger1_value_{};
- float run_trigger2_value_{};
int left_button_{-1};
int right_button_{-1};
int up_button_{-1};
@@ -167,15 +160,19 @@ class JoystickInput : public InputDevice {
int right_button2_{-1};
int up_button2_{-1};
int down_button2_{-1};
- std::set run_buttons_held_;
int sdl_joystick_id_{};
- bool ps3_jaxis1_pressed_{};
- bool ps3_jaxis2_pressed_{};
+ float run_value_{};
+ float run_trigger1_min_{};
+ float run_trigger1_max_{};
+ float run_trigger2_min_{};
+ float run_trigger2_max_{};
+ float run_trigger1_value_{};
+ float run_trigger2_value_{};
float calibration_threshold_{};
float calibration_break_threshold_{};
float analog_calibration_vals_[kJoystickAnalogCalibrationDivisions]{};
- std::string custom_device_name_;
- bool can_configure_{};
+ float calibrated_neutral_x_{};
+ float calibrated_neutral_y_{};
int32_t dialog_jaxis_x_{};
int32_t dialog_jaxis_y_{};
int32_t jaxis_raw_x_{};
@@ -183,11 +180,12 @@ class JoystickInput : public InputDevice {
int32_t jaxis_x_{};
int32_t jaxis_y_{};
millisecs_t calibration_start_time_x_{};
- float calibrated_neutral_x_{};
millisecs_t calibration_start_time_y_{};
- float calibrated_neutral_y_{};
- bool resetting_{};
- bool calibrate_{};
+ std::set run_buttons_held_;
+ std::string custom_device_name_;
+ std::string raw_sdl_joystick_name_;
+ std::string raw_sdl_joystick_identifier_;
+ Object::Ref ui_repeater_;
BA_DISALLOW_CLASS_COPIES(JoystickInput);
};
diff --git a/src/ballistica/base/input/device/keyboard_input.cc b/src/ballistica/base/input/device/keyboard_input.cc
index 4a1908d4..ba9b3cab 100644
--- a/src/ballistica/base/input/device/keyboard_input.cc
+++ b/src/ballistica/base/input/device/keyboard_input.cc
@@ -2,8 +2,10 @@
#include "ballistica/base/input/device/keyboard_input.h"
+#include "ballistica/base/app_adapter/app_adapter.h"
#include "ballistica/base/platform/base_platform.h"
#include "ballistica/base/support/classic_soft.h"
+#include "ballistica/base/support/repeater.h"
#include "ballistica/base/ui/ui.h"
namespace ballistica::base {
@@ -42,10 +44,12 @@ KeyboardInput::KeyboardInput(KeyboardInput* parent_keyboard_input_in) {
KeyboardInput::~KeyboardInput() = default;
-auto KeyboardInput::HandleKey(const SDL_Keysym* keysym, bool repeat, bool down)
- -> bool {
+auto KeyboardInput::HandleKey(const SDL_Keysym* keysym, bool down) -> bool {
// Only allow the *main* keyboard to talk to the UI
if (parent_keyboard_input_ == nullptr) {
+ // Any new event coming in cancels repeats.
+ ui_repeater_.Clear();
+
if (g_base->ui->GetWidgetForInput(this)) {
bool pass = false;
auto c = WidgetMessage::Type::kEmptyMessage;
@@ -78,10 +82,8 @@ auto KeyboardInput::HandleKey(const SDL_Keysym* keysym, bool repeat, bool down)
case SDLK_SPACE:
case SDLK_KP_ENTER:
case SDLK_RETURN:
- if (!repeat) {
- c = WidgetMessage::Type::kActivate;
- pass = true;
- }
+ c = WidgetMessage::Type::kActivate;
+ pass = true;
break;
case SDLK_ESCAPE:
// (limit to kb1 so we don't get double-beeps on failure)
@@ -123,32 +125,44 @@ auto KeyboardInput::HandleKey(const SDL_Keysym* keysym, bool repeat, bool down)
}
}
if (pass) {
- g_base->ui->SendWidgetMessage(WidgetMessage(c, keysym));
+ // For movement and key press widget events, set up repeats.
+ // Otherwise run a single time immediately.
+ switch (c) {
+ case WidgetMessage::Type::kMoveUp:
+ case WidgetMessage::Type::kMoveDown:
+ case WidgetMessage::Type::kMoveLeft:
+ case WidgetMessage::Type::kMoveRight:
+ case WidgetMessage::Type::kKey:
+ // Note: Need to pass keysym along as a value; not a pointer.
+ ui_repeater_ = Repeater::New(
+ g_base->app_adapter->GetKeyRepeatDelay(),
+ g_base->app_adapter->GetKeyRepeatInterval(),
+ [c, keysym = *keysym] {
+ g_base->ui->SendWidgetMessage(WidgetMessage(c, &keysym));
+ });
+ break;
+ default:
+ g_base->ui->SendWidgetMessage(WidgetMessage(c, keysym));
+ break;
+ }
}
return (pass);
}
}
// Bring up menu if start is pressed.
- if (keysym->sym == start_key_ && !repeat && !g_base->ui->MainMenuVisible()) {
+ if (keysym->sym == start_key_ && !g_base->ui->MainMenuVisible()) {
g_base->ui->PushMainMenuPressCall(this);
return true;
}
-// Clion seems to think child_keyboard_input_ will never be set here (it will).
-#pragma clang diagnostic push
-#pragma ide diagnostic ignored "UnreachableCode"
-#pragma ide diagnostic ignored "ConstantConditionsOC"
-
// At this point, if we have a child input, let it try to handle things.
if (child_keyboard_input_ && enable_child_) {
- if (child_keyboard_input_->HandleKey(keysym, repeat, down)) {
+ if (child_keyboard_input_->HandleKey(keysym, down)) {
return true;
}
}
-#pragma clang diagnostic pop
-
if (!AttachedToPlayer()) {
if (down
&& ((keysym->sym == jump_key_) || (keysym->sym == punch_key_)
@@ -173,152 +187,151 @@ auto KeyboardInput::HandleKey(const SDL_Keysym* keysym, bool repeat, bool down)
// (removing init values from input_type and input_type_2 gives a
// 'possibly uninited value used' warning but leaving them gives a
// 'values unused' warning. Grumble.)
- explicit_bool(input_type
- == (explicit_bool(false) ? input_type_2 : InputType::kLast));
+ // explicit_bool(input_type
+ // == (explicit_bool(false) ? input_type_2 :
+ // InputType::kLast));
- if (!repeat) {
- // Keyboard 1 supports assigned keys plus arrow keys if they're unused.
- if (keysym->sym == left_key_
- || (device_number() == 1 && keysym->sym == SDLK_LEFT
- && !left_key_assigned())) {
- player_input = true;
- input_type = InputType::kLeftRight;
- left_held_ = down;
- if (down) {
- if (right_held_) {
- input_value = 0;
- } else {
- input_value = -32767;
- }
+ // Keyboard 1 supports assigned keys plus arrow keys if they're unused.
+ if (keysym->sym == left_key_
+ || (device_number() == 1 && keysym->sym == SDLK_LEFT
+ && !left_key_assigned())) {
+ player_input = true;
+ input_type = InputType::kLeftRight;
+ left_held_ = down;
+ if (down) {
+ if (right_held_) {
+ input_value = 0;
} else {
- if (right_held_) {
- input_value = 32767;
- }
- }
- } else if (keysym->sym == right_key_
- || (device_number() == 1 && keysym->sym == SDLK_RIGHT
- && !right_key_assigned())) {
- // Keyboard 1 supports assigned keys plus arrow keys if they're unused.
- player_input = true;
- input_type = InputType::kLeftRight;
- right_held_ = down;
- if (down) {
- if (left_held_) {
- input_value = 0;
- } else {
- input_value = 32767;
- }
- } else {
- if (left_held_) {
- input_value = -32767;
- }
- }
- } else if (keysym->sym == up_key_
- || (device_number() == 1 && keysym->sym == SDLK_UP
- && !up_key_assigned())) {
- player_input = true;
- input_type = InputType::kUpDown;
- up_held_ = down;
- if (down) {
- if (down_held_) {
- input_value = 0;
- } else {
- input_value = 32767;
- }
- } else {
- if (down_held_) input_value = -32767;
- }
- } else if (keysym->sym == down_key_
- || (device_number() == 1 && keysym->sym == SDLK_DOWN
- && !down_key_assigned())) {
- player_input = true;
- input_type = InputType::kUpDown;
- down_held_ = down;
- if (down) {
- if (up_held_) {
- input_value = 0;
- } else {
- input_value = -32767;
- }
- } else {
- if (up_held_) input_value = 32767;
- }
- } else if (keysym->sym == punch_key_) {
- player_input = true;
- UpdateRun(keysym->sym, down);
- if (down) {
- input_type = InputType::kPunchPress;
- } else {
- input_type = InputType::kPunchRelease;
- }
- } else if (keysym->sym == bomb_key_) {
- player_input = true;
- UpdateRun(keysym->sym, down);
- if (down)
- input_type = InputType::kBombPress;
- else
- input_type = InputType::kBombRelease;
- } else if (keysym->sym == hold_position_key_) {
- player_input = true;
- if (down) {
- input_type = InputType::kHoldPositionPress;
- } else {
- input_type = InputType::kHoldPositionRelease;
- }
- } else if (keysym->sym == pick_up_key_) {
- player_input = true;
- UpdateRun(keysym->sym, down);
- if (down) {
- input_type = InputType::kPickUpPress;
- } else {
- input_type = InputType::kPickUpRelease;
- }
- } else if ((device_number() == 1 && keysym->sym == SDLK_RETURN)
- || (device_number() == 1 && keysym->sym == SDLK_KP_ENTER)
- || keysym->sym == jump_key_) {
- // Keyboard 1 claims certain keys if they are otherwise unclaimed
- // (arrow keys, enter/return, etc).
- player_input = true;
- UpdateRun(keysym->sym, down);
- if (down) {
- input_type = InputType::kJumpPress;
- have_input_2 = true;
- input_type_2 = InputType::kFlyPress;
- } else {
- input_type = InputType::kJumpRelease;
- have_input_2 = true;
- input_type_2 = InputType::kFlyRelease;
+ input_value = -32767;
}
} else {
- // Any other keys get processed as run keys.
- // keypad keys go to player 2 - anything else to player 1.
- switch (keysym->sym) {
- case SDLK_KP_0:
- case SDLK_KP_1:
- case SDLK_KP_2:
- case SDLK_KP_3:
- case SDLK_KP_4:
- case SDLK_KP_5:
- case SDLK_KP_6:
- case SDLK_KP_7:
- case SDLK_KP_8:
- case SDLK_KP_9:
- case SDLK_KP_PLUS:
- case SDLK_KP_MINUS:
- case SDLK_KP_ENTER:
- if (device_number() == 2) {
- UpdateRun(keysym->sym, down);
- return true;
- }
- break;
- default:
- if (device_number() == 1) {
- UpdateRun(keysym->sym, down);
- return true;
- }
- break;
+ if (right_held_) {
+ input_value = 32767;
}
}
+ } else if (keysym->sym == right_key_
+ || (device_number() == 1 && keysym->sym == SDLK_RIGHT
+ && !right_key_assigned())) {
+ // Keyboard 1 supports assigned keys plus arrow keys if they're unused.
+ player_input = true;
+ input_type = InputType::kLeftRight;
+ right_held_ = down;
+ if (down) {
+ if (left_held_) {
+ input_value = 0;
+ } else {
+ input_value = 32767;
+ }
+ } else {
+ if (left_held_) {
+ input_value = -32767;
+ }
+ }
+ } else if (keysym->sym == up_key_
+ || (device_number() == 1 && keysym->sym == SDLK_UP
+ && !up_key_assigned())) {
+ player_input = true;
+ input_type = InputType::kUpDown;
+ up_held_ = down;
+ if (down) {
+ if (down_held_) {
+ input_value = 0;
+ } else {
+ input_value = 32767;
+ }
+ } else {
+ if (down_held_) input_value = -32767;
+ }
+ } else if (keysym->sym == down_key_
+ || (device_number() == 1 && keysym->sym == SDLK_DOWN
+ && !down_key_assigned())) {
+ player_input = true;
+ input_type = InputType::kUpDown;
+ down_held_ = down;
+ if (down) {
+ if (up_held_) {
+ input_value = 0;
+ } else {
+ input_value = -32767;
+ }
+ } else {
+ if (up_held_) input_value = 32767;
+ }
+ } else if (keysym->sym == punch_key_) {
+ player_input = true;
+ UpdateRun_(keysym->sym, down);
+ if (down) {
+ input_type = InputType::kPunchPress;
+ } else {
+ input_type = InputType::kPunchRelease;
+ }
+ } else if (keysym->sym == bomb_key_) {
+ player_input = true;
+ UpdateRun_(keysym->sym, down);
+ if (down)
+ input_type = InputType::kBombPress;
+ else
+ input_type = InputType::kBombRelease;
+ } else if (keysym->sym == hold_position_key_) {
+ player_input = true;
+ if (down) {
+ input_type = InputType::kHoldPositionPress;
+ } else {
+ input_type = InputType::kHoldPositionRelease;
+ }
+ } else if (keysym->sym == pick_up_key_) {
+ player_input = true;
+ UpdateRun_(keysym->sym, down);
+ if (down) {
+ input_type = InputType::kPickUpPress;
+ } else {
+ input_type = InputType::kPickUpRelease;
+ }
+ } else if ((device_number() == 1 && keysym->sym == SDLK_RETURN)
+ || (device_number() == 1 && keysym->sym == SDLK_KP_ENTER)
+ || keysym->sym == jump_key_) {
+ // Keyboard 1 claims certain keys if they are otherwise unclaimed
+ // (arrow keys, enter/return, etc).
+ player_input = true;
+ UpdateRun_(keysym->sym, down);
+ if (down) {
+ input_type = InputType::kJumpPress;
+ have_input_2 = true;
+ input_type_2 = InputType::kFlyPress;
+ } else {
+ input_type = InputType::kJumpRelease;
+ have_input_2 = true;
+ input_type_2 = InputType::kFlyRelease;
+ }
+ } else {
+ // Any other keys get processed as run keys.
+ // keypad keys go to player 2 - anything else to player 1.
+ switch (keysym->sym) {
+ case SDLK_KP_0:
+ case SDLK_KP_1:
+ case SDLK_KP_2:
+ case SDLK_KP_3:
+ case SDLK_KP_4:
+ case SDLK_KP_5:
+ case SDLK_KP_6:
+ case SDLK_KP_7:
+ case SDLK_KP_8:
+ case SDLK_KP_9:
+ case SDLK_KP_PLUS:
+ case SDLK_KP_MINUS:
+ case SDLK_KP_ENTER:
+ if (device_number() == 2) {
+ UpdateRun_(keysym->sym, down);
+ return true;
+ }
+ break;
+ default:
+ if (device_number() == 1) {
+ UpdateRun_(keysym->sym, down);
+ return true;
+ }
+ break;
+ }
}
if (player_input) {
@@ -344,7 +357,7 @@ void KeyboardInput::ResetHeldStates() {
}
}
-void KeyboardInput::UpdateRun(SDL_Keycode key, bool down) {
+void KeyboardInput::UpdateRun_(SDL_Keycode key, bool down) {
bool was_held = (!keys_held_.empty());
if (down) {
keys_held_.insert(key);
@@ -407,51 +420,51 @@ void KeyboardInput::UpdateMapping() {
int val = cl ? cl->GetControllerValue(this, "buttonJump") : -1;
jump_key_ = (val == -1) ? jump_key_default : (SDL_Keycode)val;
- UpdateArrowKeys(jump_key_);
+ UpdateArrowKeys_(jump_key_);
val = cl ? cl->GetControllerValue(this, "buttonPunch") : -1;
punch_key_ = (val == -1) ? punch_key_default : (SDL_Keycode)val;
- UpdateArrowKeys(punch_key_);
+ UpdateArrowKeys_(punch_key_);
val = cl ? cl->GetControllerValue(this, "buttonBomb") : -1;
bomb_key_ = (val == -1) ? bomb_key_default : (SDL_Keycode)val;
- UpdateArrowKeys(bomb_key_);
+ UpdateArrowKeys_(bomb_key_);
val = cl ? cl->GetControllerValue(this, "buttonPickUp") : -1;
pick_up_key_ = (val == -1) ? pick_up_key_default : (SDL_Keycode)val;
- UpdateArrowKeys(pick_up_key_);
+ UpdateArrowKeys_(pick_up_key_);
val = cl ? cl->GetControllerValue(this, "buttonHoldPosition") : -1;
hold_position_key_ =
(val == -1) ? hold_position_key_default : (SDL_Keycode)val;
- UpdateArrowKeys(hold_position_key_);
+ UpdateArrowKeys_(hold_position_key_);
val = cl ? cl->GetControllerValue(this, "buttonStart") : -1;
start_key_ = (val == -1) ? start_key_default : (SDL_Keycode)val;
- UpdateArrowKeys(start_key_);
+ UpdateArrowKeys_(start_key_);
val = cl ? cl->GetControllerValue(this, "buttonUp") : -1;
up_key_ = (val == -1) ? up_key_default : (SDL_Keycode)val;
- UpdateArrowKeys(up_key_);
+ UpdateArrowKeys_(up_key_);
val = cl ? cl->GetControllerValue(this, "buttonDown") : -1;
down_key_ = (val == -1) ? down_key_default : (SDL_Keycode)val;
- UpdateArrowKeys(down_key_);
+ UpdateArrowKeys_(down_key_);
val = cl ? cl->GetControllerValue(this, "buttonLeft") : -1;
left_key_ = (val == -1) ? left_key_default : (SDL_Keycode)val;
- UpdateArrowKeys(left_key_);
+ UpdateArrowKeys_(left_key_);
val = cl ? cl->GetControllerValue(this, "buttonRight") : -1;
right_key_ = (val == -1) ? right_key_default : (SDL_Keycode)val;
- UpdateArrowKeys(right_key_);
+ UpdateArrowKeys_(right_key_);
enable_child_ = true;
up_held_ = down_held_ = left_held_ = right_held_ = false;
}
-void KeyboardInput::UpdateArrowKeys(SDL_Keycode key) {
+void KeyboardInput::UpdateArrowKeys_(SDL_Keycode key) {
if (key == SDLK_UP) {
up_key_assigned_ = true;
} else if (key == SDLK_DOWN) {
@@ -465,7 +478,6 @@ void KeyboardInput::UpdateArrowKeys(SDL_Keycode key) {
auto KeyboardInput::GetButtonName(int index) -> std::string {
return g_base->platform->GetKeyName(index);
- // return InputDevice::GetButtonName(index);
}
auto KeyboardInput::GetRawDeviceName() -> std::string { return "Keyboard"; }
diff --git a/src/ballistica/base/input/device/keyboard_input.h b/src/ballistica/base/input/device/keyboard_input.h
index 6d790e9c..8138b5c6 100644
--- a/src/ballistica/base/input/device/keyboard_input.h
+++ b/src/ballistica/base/input/device/keyboard_input.h
@@ -8,6 +8,7 @@
#include "ballistica/base/input/device/input_device.h"
#include "ballistica/core/platform/support/min_sdl.h"
+#include "ballistica/shared/foundation/object.h"
namespace ballistica::base {
@@ -15,7 +16,7 @@ class KeyboardInput : public InputDevice {
public:
explicit KeyboardInput(KeyboardInput* parent);
~KeyboardInput() override;
- auto HandleKey(const SDL_Keysym* keysym, bool repeat, bool down) -> bool;
+ auto HandleKey(const SDL_Keysym* keysym, bool down) -> bool;
void UpdateMapping() override;
auto GetRawDeviceName() -> std::string override;
void ResetHeldStates() override;
@@ -29,8 +30,17 @@ class KeyboardInput : public InputDevice {
auto GetButtonName(int index) -> std::string override;
private:
- void UpdateArrowKeys(SDL_Keycode key);
- void UpdateRun(SDL_Keycode key, bool down);
+ void UpdateArrowKeys_(SDL_Keycode key);
+ void UpdateRun_(SDL_Keycode key, bool down);
+ bool down_held_ : 1 {};
+ bool up_held_ : 1 {};
+ bool left_held_ : 1 {};
+ bool right_held_ : 1 {};
+ bool enable_child_ : 1 {};
+ bool left_key_assigned_ : 1 {};
+ bool right_key_assigned_ : 1 {};
+ bool up_key_assigned_ : 1 {};
+ bool down_key_assigned_ : 1 {};
SDL_Keycode up_key_{};
SDL_Keycode down_key_{};
SDL_Keycode left_key_{};
@@ -41,18 +51,10 @@ class KeyboardInput : public InputDevice {
SDL_Keycode pick_up_key_{};
SDL_Keycode hold_position_key_{};
SDL_Keycode start_key_{};
- bool down_held_{};
- bool up_held_{};
- bool left_held_{};
- bool right_held_{};
- bool enable_child_{};
- bool left_key_assigned_{};
- bool right_key_assigned_{};
- bool up_key_assigned_{};
- bool down_key_assigned_{};
KeyboardInput* parent_keyboard_input_{};
KeyboardInput* child_keyboard_input_{};
std::set keys_held_;
+ Object::Ref ui_repeater_;
};
} // namespace ballistica::base
diff --git a/src/ballistica/base/input/input.cc b/src/ballistica/base/input/input.cc
index acefa84b..dfca9fd0 100644
--- a/src/ballistica/base/input/input.cc
+++ b/src/ballistica/base/input/input.cc
@@ -159,12 +159,11 @@ void Input::AnnounceConnects_() {
// For the first announcement just say "X controllers detected" and don't
// have a sound.
- if (first_print && g_core->GetAppTimeSeconds() < 5.0) {
+ if (first_print && g_core->GetAppTimeSeconds() < 2.0) {
first_print = false;
// Disabling this completely on Android for now; we often get large
// numbers of devices there that aren't actually devices.
-
bool do_print_initial_counts{!g_buildconfig.ostype_android()};
// If there's been several connected, just give a number.
@@ -243,7 +242,7 @@ 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_(); }).Get());
}
void Input::ShowStandardInputDeviceDisconnectedMessage_(InputDevice* j) {
@@ -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_(); }).Get());
}
void Input::PushAddInputDeviceCall(InputDevice* input_device,
@@ -802,6 +801,7 @@ void Input::ProcessStressTesting(int player_count) {
(*test_input).Reset();
}
}
+
while (stress_test_time_ < time) {
stress_test_time_++;
for (auto& test_input : test_inputs_) {
@@ -815,7 +815,7 @@ void Input::PushTextInputEvent(const std::string& text) {
g_base->logic->event_loop()->PushCall([this, text] {
MarkInputActive();
- // If if the app doesn't want direct text input right now.
+ // If the app doesn't want direct text input right now, ignore.
if (!g_base->app_adapter->HasDirectKeyboardInput()) {
return;
}
@@ -848,6 +848,7 @@ void Input::PushTextInputEvent(const std::string& text) {
&& g_base->ui->dev_console()->HandleTextEditing(text)) {
return;
}
+
g_base->ui->SendWidgetMessage(WidgetMessage(
WidgetMessage::Type::kTextInput, nullptr, 0, 0, 0, 0, text.c_str()));
});
@@ -986,6 +987,34 @@ void Input::HandleKeyPress_(const SDL_Keysym& keysym) {
return;
}
+ // Nowadays we don't want the OS to deliver repeat events to us,
+ // so filter out any that we get and make noise that they should stop. We
+ // explicitly handle repeats for UI purposes at the InputDevice or Widget
+ // level now.
+ if (keys_held_.find(keysym.sym) != keys_held_.end()) {
+ // Look out for several repeats coming in within the span of a few
+ // seconds and complain if it happens. This should allow for the random
+ // fluke repeat key press event due to funky OS circumstances.
+ static int count{};
+ static seconds_t last_count_reset_time{};
+ auto now = g_core->GetAppTimeSeconds();
+ if (now - last_count_reset_time > 2.0) {
+ count = 0;
+ last_count_reset_time = now;
+ } else {
+ count++;
+ if (count > 10) {
+ BA_LOG_ONCE(
+ LogLevel::kWarning,
+ "Input::HandleKeyPress_ seems to be getting passed repeat key"
+ " press events. Only initial press events should be passed.");
+ }
+ }
+ return;
+ }
+
+ keys_held_.insert(keysym.sym);
+
// If someone is capturing these events, give them a crack at it.
if (keyboard_input_capture_press_) {
if (keyboard_input_capture_press_(keysym)) {
@@ -998,52 +1027,44 @@ void Input::HandleKeyPress_(const SDL_Keysym& keysym) {
// ideally we should use the modifiers bundled with the key presses)
UpdateModKeyStates_(&keysym, true);
- bool repeat_press;
- if (keys_held_.count(keysym.sym) != 0) {
- repeat_press = true;
- } else {
- repeat_press = false;
- keys_held_.insert(keysym.sym);
- }
-
// Mobile-specific stuff.
- if (g_buildconfig.ostype_ios_tvos() || g_buildconfig.ostype_android()) {
- switch (keysym.sym) {
- // FIXME: See if this stuff is still necessary. Was this perhaps
- // specifically to support the console?
- case SDLK_DELETE:
- case SDLK_RETURN:
- case SDLK_KP_ENTER:
- case SDLK_BACKSPACE: {
- // FIXME: I don't remember what this was put here for, but now that
- // we have hardware keyboards it crashes text fields by sending
- // 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));
- break;
- }
- default:
- break;
- }
- }
+ // if (g_buildconfig.ostype_ios_tvos() || g_buildconfig.ostype_android()) {
+ // switch (keysym.sym) {
+ // // FIXME: See if this stuff is still necessary. Was this perhaps
+ // // specifically to support the console?
+ // case SDLK_DELETE:
+ // case SDLK_RETURN:
+ // case SDLK_KP_ENTER:
+ // case SDLK_BACKSPACE: {
+ // // FIXME: I don't remember what this was put here for, but now that
+ // // we have hardware keyboards it crashes text fields by sending
+ // // 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));
+ // break;
+ // }
+ // default:
+ // break;
+ // }
+ // }
// Explicitly handle fullscreen-toggles in some cases.
if (g_base->app_adapter->FullscreenControlAvailable()) {
bool do_toggle{};
// On our Mac SDL builds we support ctrl+F for toggling fullscreen.
// On our nice Cocoa build, fullscreening happens magically through the
- // view menu fullscreen controls.
+ // view menu fullscreen control's shortcut.
if (g_buildconfig.ostype_macos() && !g_buildconfig.xcode_build()) {
- if (!repeat_press && keysym.sym == SDLK_f && ((keysym.mod & KMOD_CTRL))) {
+ if (keysym.sym == SDLK_f && ((keysym.mod & KMOD_CTRL))) {
do_toggle = true;
}
}
- // On Windows we support both F11 and Alt+Enter for toggling fullscreen.
- if (g_buildconfig.ostype_windows()) {
- if (!repeat_press
- && (keysym.sym == SDLK_F11
- || (keysym.sym == SDLK_RETURN && ((keysym.mod & KMOD_ALT))))) {
+ // On Windows and Linux we support both F11 and Alt+Enter for toggling
+ // fullscreen.
+ if (g_buildconfig.ostype_windows() || g_buildconfig.ostype_linux()) {
+ if ((keysym.sym == SDLK_F11
+ || (keysym.sym == SDLK_RETURN && ((keysym.mod & KMOD_ALT))))) {
do_toggle = true;
}
}
@@ -1055,26 +1076,21 @@ void Input::HandleKeyPress_(const SDL_Keysym& keysym) {
}
}
- // Control-Q quits. On Mac, the usual Cmd-Q gets handled implicitly by the
- // app-adapter.
- // UPDATE: Disabling this for now. Looks like standard OS shortcuts like
- // Alt+F4 on windows or Cmd-Q on Mac are doing the right thing with SDL
- // builds these days so these are not needed.
- // 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.
+ // Dev Console.
if (auto* console = g_base->ui->dev_console()) {
+ if (keysym.sym == SDLK_BACKQUOTE || keysym.sym == SDLK_F2) {
+ // (reset input so characters don't continue walking and stuff)
+ g_base->input->ResetHoldStates();
+ console->ToggleState();
+ return;
+ }
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
+ if (keysym.sym == SDLK_v
&& ((keysym.mod & KMOD_CTRL) || (keysym.mod & KMOD_GUI))) {
g_base->ui->SendWidgetMessage(WidgetMessage(WidgetMessage::Type::kPaste));
return;
@@ -1082,93 +1098,89 @@ void Input::HandleKeyPress_(const SDL_Keysym& keysym) {
bool handled = false;
- // None of the following stuff accepts key repeats.
- if (!repeat_press) {
- switch (keysym.sym) {
- // Menu button on android/etc. pops up the menu.
- case SDLK_MENU: {
- if (!g_base->ui->MainMenuVisible()) {
- g_base->ui->PushMainMenuPressCall(touch_input_);
- }
- handled = true;
- break;
+ switch (keysym.sym) {
+ // Menu button on android/etc. pops up the menu.
+ case SDLK_MENU: {
+ if (!g_base->ui->MainMenuVisible()) {
+ g_base->ui->PushMainMenuPressCall(touch_input_);
}
-
- case SDLK_EQUALS:
- case SDLK_PLUS:
- if (keysym.mod & KMOD_CTRL) {
- g_base->app_mode()->ChangeGameSpeed(1);
- handled = true;
- }
- break;
-
- case SDLK_MINUS:
- if (keysym.mod & KMOD_CTRL) {
- g_base->app_mode()->ChangeGameSpeed(-1);
- handled = true;
- }
- break;
-
- case SDLK_F5: {
- if (g_base->ui->PartyIconVisible()) {
- g_base->ui->ActivatePartyIcon();
- }
- handled = true;
- break;
- }
-
- case SDLK_F7:
- assert(g_base->logic->event_loop());
- g_base->logic->event_loop()->PushCall(
- [] { g_base->graphics->ToggleManualCamera(); });
- handled = true;
- break;
-
- case SDLK_F8:
- assert(g_base->logic->event_loop());
- g_base->logic->event_loop()->PushCall(
- [] { g_base->graphics->ToggleNetworkDebugDisplay(); });
- handled = true;
- break;
-
- case SDLK_F9:
- g_base->python->objs().PushCall(
- BasePython::ObjID::kLanguageTestToggleCall);
- handled = true;
- break;
-
- case SDLK_F10:
- assert(g_base->logic->event_loop());
- g_base->logic->event_loop()->PushCall(
- [] { g_base->graphics->ToggleDebugDraw(); });
- handled = true;
- break;
-
- case SDLK_ESCAPE:
-
- if (!g_base->ui->MainMenuVisible()) {
- // There's no main menu up. Ask for one.
-
- // Note: keyboard_input_ may be nullptr but escape key should
- // still function for menus; it just won't claim ownership.
- g_base->ui->PushMainMenuPressCall(keyboard_input_);
- } else {
- // Ok there *is* a main menu up. Send it a cancel message.
- g_base->ui->SendWidgetMessage(
- WidgetMessage(WidgetMessage::Type::kCancel));
- }
- handled = true;
- break;
-
- default:
- break;
+ handled = true;
+ break;
}
+
+ case SDLK_EQUALS:
+ case SDLK_PLUS:
+ if (keysym.mod & KMOD_CTRL) {
+ g_base->app_mode()->ChangeGameSpeed(1);
+ handled = true;
+ }
+ break;
+
+ case SDLK_MINUS:
+ if (keysym.mod & KMOD_CTRL) {
+ g_base->app_mode()->ChangeGameSpeed(-1);
+ handled = true;
+ }
+ break;
+
+ case SDLK_F5: {
+ if (g_base->ui->PartyIconVisible()) {
+ g_base->ui->ActivatePartyIcon();
+ }
+ handled = true;
+ break;
+ }
+
+ case SDLK_F7:
+ assert(g_base->logic->event_loop());
+ g_base->logic->event_loop()->PushCall(
+ [] { g_base->graphics->ToggleManualCamera(); });
+ handled = true;
+ break;
+
+ case SDLK_F8:
+ assert(g_base->logic->event_loop());
+ g_base->logic->event_loop()->PushCall(
+ [] { g_base->graphics->ToggleNetworkDebugDisplay(); });
+ handled = true;
+ break;
+
+ case SDLK_F9:
+ g_base->python->objs().PushCall(
+ BasePython::ObjID::kLanguageTestToggleCall);
+ handled = true;
+ break;
+
+ case SDLK_F10:
+ assert(g_base->logic->event_loop());
+ g_base->logic->event_loop()->PushCall(
+ [] { g_base->graphics->ToggleDebugDraw(); });
+ handled = true;
+ break;
+
+ case SDLK_ESCAPE:
+ if (!g_base->ui->MainMenuVisible()) {
+ // There's no main menu up. Ask for one.
+
+ // Note: keyboard_input_ may be nullptr but escape key should
+ // still function for menus; it just won't claim ownership.
+ g_base->ui->PushMainMenuPressCall(keyboard_input_);
+ } else {
+ // Ok there *is* a main menu up. Send it a cancel message.
+ g_base->ui->SendWidgetMessage(
+ WidgetMessage(WidgetMessage::Type::kCancel));
+ }
+ handled = true;
+ break;
+
+ default:
+ break;
}
- // If we haven't claimed it, pass it along as potential player/widget input.
+ // If we haven't handled this, pass it along as potential player/widget input.
if (!handled) {
if (keyboard_input_) {
- keyboard_input_->HandleKey(&keysym, repeat_press, true);
+ keyboard_input_->HandleKey(&keysym, true);
}
}
}
@@ -1184,7 +1196,7 @@ 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_.find(keysym.sym) == keys_held_.end()) {
return;
}
@@ -1205,7 +1217,7 @@ void Input::HandleKeyRelease_(const SDL_Keysym& keysym) {
}
if (keyboard_input_) {
- keyboard_input_->HandleKey(&keysym, false, false);
+ keyboard_input_->HandleKey(&keysym, false);
}
}
diff --git a/src/ballistica/base/logic/logic.cc b/src/ballistica/base/logic/logic.cc
index 67a05cf2..030229c2 100644
--- a/src/ballistica/base/logic/logic.cc
+++ b/src/ballistica/base/logic/logic.cc
@@ -100,7 +100,7 @@ void Logic::OnGraphicsReady() {
// variety of rates anyway. NOTE: This length is currently milliseconds.
headless_display_time_step_timer_ = event_loop()->NewTimer(
kHeadlessMinDisplayTimeStep / 1000, true,
- NewLambdaRunnable([this] { StepDisplayTime_(); }));
+ NewLambdaRunnable([this] { StepDisplayTime_(); }).Get());
} else {
// In gui mode, push an initial frame to the graphics server. From this
// point it will be self-sustaining, sending us a frame request each
@@ -133,9 +133,9 @@ void Logic::CompleteAppBootstrapping_() {
// Set up our timers.
process_pending_work_timer_ = event_loop()->NewTimer(
- 0, true, NewLambdaRunnable([this] { ProcessPendingWork_(); }));
+ 0, true, NewLambdaRunnable([this] { ProcessPendingWork_(); }).Get());
asset_prune_timer_ = event_loop()->NewTimer(
- 2345, true, NewLambdaRunnable([] { g_base->assets->Prune(); }));
+ 2345, true, NewLambdaRunnable([] { g_base->assets->Prune(); }).Get());
// Let our initial dummy app-mode know it has become active.
g_base->app_mode()->OnActivate();
@@ -635,8 +635,8 @@ void Logic::NotifyOfPendingAssetLoads() {
UpdatePendingWorkTimer_();
}
-auto Logic::NewAppTimer(millisecs_t length, bool repeat,
- const Object::Ref& runnable) -> int {
+auto Logic::NewAppTimer(millisecs_t length, bool repeat, Runnable* runnable)
+ -> int {
// App-Timers simply get injected into our loop and run alongside our own
// stuff.
assert(g_base->InLogicThread());
@@ -667,7 +667,7 @@ auto Logic::NewDisplayTimer(microsecs_t length, bool repeat,
assert(g_base->InLogicThread());
int offset = 0;
Timer* t = display_timers_->NewTimer(g_core->GetAppTimeMicrosecs(), length,
- offset, repeat ? -1 : 0, runnable);
+ offset, repeat ? -1 : 0, runnable.Get());
return t->id();
}
diff --git a/src/ballistica/base/logic/logic.h b/src/ballistica/base/logic/logic.h
index b215be32..3974392b 100644
--- a/src/ballistica/base/logic/logic.h
+++ b/src/ballistica/base/logic/logic.h
@@ -89,8 +89,7 @@ class Logic {
void HandleInterruptSignal();
void HandleTerminateSignal();
- auto NewAppTimer(millisecs_t length, bool repeat,
- const Object::Ref& runnable) -> int;
+ auto NewAppTimer(millisecs_t length, bool repeat, Runnable* runnable) -> int;
void DeleteAppTimer(int timer_id);
void SetAppTimerLength(int timer_id, millisecs_t length);
diff --git a/src/ballistica/base/platform/apple/base_platform_apple.cc b/src/ballistica/base/platform/apple/base_platform_apple.cc
index 55226812..833d8003 100644
--- a/src/ballistica/base/platform/apple/base_platform_apple.cc
+++ b/src/ballistica/base/platform/apple/base_platform_apple.cc
@@ -4,11 +4,14 @@
#include "ballistica/base/platform/apple/base_platform_apple.h"
#if BA_XCODE_BUILD
-#include
+#include "ballistica/base/platform/apple/apple_utils.h"
+#include "ballistica/base/platform/apple/from_swift.h"
#endif
#if BA_XCODE_BUILD
-#include "ballistica/base/platform/apple/apple_utils.h"
+// This needs to be below ballistica headers since it relies on
+// some types in them but does not include headers itself.
+#include
#endif
namespace ballistica::base {
@@ -48,9 +51,9 @@ void BasePlatformApple::PurchaseAck(const std::string& purchase,
void BasePlatformApple::DoOpenURL(const std::string& url) {
#if BA_XCODE_BUILD
#if BA_OSTYPE_MACOS
- BallisticaKit::CocoaFromCppOpenURL(url);
+ BallisticaKit::CocoaFromCpp::OpenURL(url);
#else
- BallisticaKit::UIKitFromCppOpenURL(url);
+ BallisticaKit::UIKitFromCpp::OpenURL(url);
#endif // BA_OSTYPE_MACOS
#else
diff --git a/src/ballistica/base/python/class/python_class_app_timer.cc b/src/ballistica/base/python/class/python_class_app_timer.cc
index dd75823f..0202a91d 100644
--- a/src/ballistica/base/python/class/python_class_app_timer.cc
+++ b/src/ballistica/base/python/class/python_class_app_timer.cc
@@ -95,7 +95,7 @@ auto PythonClassAppTimer::tp_new(PyTypeObject* type, PyObject* args,
self->timer_id_ = g_base->logic->NewAppTimer(
static_cast(length * 1000.0), repeat,
- Object::New(call_obj));
+ Object::New(call_obj).Get());
self->have_timer_ = true;
diff --git a/src/ballistica/base/python/methods/python_methods_app.cc b/src/ballistica/base/python/methods/python_methods_app.cc
index befd17d3..471c6c08 100644
--- a/src/ballistica/base/python/methods/python_methods_app.cc
+++ b/src/ballistica/base/python/methods/python_methods_app.cc
@@ -366,7 +366,7 @@ static auto PyAppTimer(PyObject* self, PyObject* args, PyObject* keywds)
}
g_base->logic->NewAppTimer(
static_cast(length * 1000.0), false,
- Object::New(call_obj));
+ Object::New(call_obj).Get());
Py_RETURN_NONE;
BA_PYTHON_CATCH;
}
@@ -1645,6 +1645,52 @@ static PyMethodDef PyAudioShutdownIsCompleteDef = {
"(internal)\n",
};
+// ----------------------- graphics_shutdown_begin -----------------------------
+
+static auto PyGraphicsShutdownBegin(PyObject* self) -> PyObject* {
+ BA_PYTHON_TRY;
+
+ g_base->app_adapter->PushGraphicsContextCall(
+ [] { g_base->graphics_server->Shutdown(); });
+
+ Py_RETURN_NONE;
+
+ BA_PYTHON_CATCH;
+}
+
+static PyMethodDef PyGraphicsShutdownBeginDef = {
+ "graphics_shutdown_begin", // name
+ (PyCFunction)PyGraphicsShutdownBegin, // method
+ METH_NOARGS, // flags
+
+ "graphics_shutdown_begin() -> None\n"
+ "\n"
+ "(internal)\n",
+};
+
+// -------------------- graphics_shutdown_is_complete --------------------------
+
+static auto PyGraphicsShutdownIsComplete(PyObject* self) -> PyObject* {
+ BA_PYTHON_TRY;
+
+ if (g_base->graphics_server->shutdown_completed()) {
+ Py_RETURN_TRUE;
+ }
+ Py_RETURN_FALSE;
+
+ BA_PYTHON_CATCH;
+}
+
+static PyMethodDef PyGraphicsShutdownIsCompleteDef = {
+ "graphics_shutdown_is_complete", // name
+ (PyCFunction)PyGraphicsShutdownIsComplete, // method
+ METH_NOARGS, // flags
+
+ "graphics_shutdown_is_complete() -> bool\n"
+ "\n"
+ "(internal)\n",
+};
+
// -----------------------------------------------------------------------------
auto PythonMethodsApp::GetMethods() -> std::vector {
@@ -1702,6 +1748,8 @@ auto PythonMethodsApp::GetMethods() -> std::vector {
PyDevConsoleInputAdapterFinishDef,
PyAudioShutdownBeginDef,
PyAudioShutdownIsCompleteDef,
+ PyGraphicsShutdownBeginDef,
+ PyGraphicsShutdownIsCompleteDef,
};
}
diff --git a/src/ballistica/base/python/methods/python_methods_graphics.cc b/src/ballistica/base/python/methods/python_methods_graphics.cc
index 63eac46f..1e489461 100644
--- a/src/ballistica/base/python/methods/python_methods_graphics.cc
+++ b/src/ballistica/base/python/methods/python_methods_graphics.cc
@@ -536,6 +536,7 @@ static auto PyFadeScreen(PyObject* self, PyObject* args, PyObject* keywds)
&endcall)) {
return nullptr;
}
+ BA_PRECONDITION(g_base->InLogicThread());
g_base->graphics->FadeScreen(static_cast(fade),
static_cast(1000.0f * time), endcall);
Py_RETURN_NONE;
diff --git a/src/ballistica/base/support/app_timer.h b/src/ballistica/base/support/app_timer.h
index e116c26a..966ef01b 100644
--- a/src/ballistica/base/support/app_timer.h
+++ b/src/ballistica/base/support/app_timer.h
@@ -13,12 +13,17 @@ namespace ballistica::base {
class AppTimer : public Object {
public:
- AppTimer(millisecs_t length, bool repeat,
- const Object::Ref& runnable) {
+ AppTimer(millisecs_t length, bool repeat, Runnable* runnable) {
assert(g_base->InLogicThread());
timer_id_ = base::g_base->logic->NewAppTimer(length, repeat, runnable);
}
+ template
+ static auto New(millisecs_t length, bool repeat, const F& lambda) {
+ return Object::New(length, repeat,
+ NewLambdaRunnable(lambda).Get());
+ }
+
void SetLength(millisecs_t length) {
assert(g_base->InLogicThread());
base::g_base->logic->SetAppTimerLength(timer_id_, length);
@@ -32,13 +37,6 @@ class AppTimer : public Object {
int timer_id_;
};
-/// Create a AppTimer from a raw lambda.
-template
-auto NewAppTimer(millisecs_t length, bool repeat, const F& lambda)
- -> Object::Ref {
- return Object::New(length, repeat, NewLambdaRunnable(lambda));
-}
-
} // namespace ballistica::base
#endif // BALLISTICA_BASE_SUPPORT_APP_TIMER_H_
diff --git a/src/ballistica/base/support/repeater.cc b/src/ballistica/base/support/repeater.cc
new file mode 100644
index 00000000..1593da15
--- /dev/null
+++ b/src/ballistica/base/support/repeater.cc
@@ -0,0 +1,58 @@
+// Released under the MIT License. See LICENSE for details.
+
+#include "ballistica/base/support/repeater.h"
+
+#include "ballistica/base/app_adapter/app_adapter.h"
+#include "ballistica/base/support/app_timer.h"
+#include "ballistica/shared/foundation/event_loop.h"
+
+namespace ballistica::base {
+Repeater::Repeater(seconds_t initial_delay, seconds_t repeat_delay,
+ Runnable* runnable)
+ : initial_delay_(initial_delay),
+ repeat_delay_(repeat_delay),
+ runnable_(runnable) {
+ assert(g_base->InLogicThread());
+ assert(initial_delay >= 0.0);
+ assert(repeat_delay >= 0.0);
+
+ // Let's go ahead and run our initial time in a deferred call;
+ // this is generally safer than running in the middle of whatever UI
+ // code set this up.
+ auto weak_this = Object::WeakRef(this);
+ g_base->logic->event_loop()->PushCall([weak_this] {
+ if (weak_this.Exists()) {
+ weak_this->runnable_->RunAndLogErrors();
+ if (!weak_this.Exists()) {
+ // Runnable might have killed us.
+ return;
+ }
+ // Kick off our initial delay timer (generally the longer one).
+ weak_this->timer_ = AppTimer::New(
+ static_cast(weak_this->initial_delay_ * 1000.0), false,
+ [weak_this] {
+ // Timer should not have fired if we died.
+ assert(weak_this.Exists());
+ weak_this->runnable_->RunAndLogErrors();
+ if (!weak_this.Exists()) {
+ // Runnable might have killed us.
+ return;
+ }
+ // Kick off our repeat timer (generally the short one).
+ weak_this->timer_ = AppTimer::New(
+ static_cast(weak_this->repeat_delay_ * 1000.0),
+ true, [weak_this] {
+ // Timer should not have fired if we died.
+ assert(weak_this.Exists());
+ weak_this->runnable_->RunAndLogErrors();
+ // Doesn't matter if Runnable killed us since we don't
+ // touch anything for the remainder of this function.
+ });
+ });
+ }
+ });
+}
+
+Repeater::~Repeater() { assert(g_base->InLogicThread()); }
+
+} // namespace ballistica::base
diff --git a/src/ballistica/base/support/repeater.h b/src/ballistica/base/support/repeater.h
new file mode 100644
index 00000000..19355a38
--- /dev/null
+++ b/src/ballistica/base/support/repeater.h
@@ -0,0 +1,35 @@
+// Released under the MIT License. See LICENSE for details.
+
+#ifndef BALLISTICA_BASE_SUPPORT_REPEATER_H_
+#define BALLISTICA_BASE_SUPPORT_REPEATER_H_
+
+#include "ballistica/base/base.h"
+#include "ballistica/shared/foundation/object.h"
+#include "ballistica/shared/generic/lambda_runnable.h"
+
+namespace ballistica::base {
+
+/// Runs some code immediately and then repeatedly after a delay. Useful for
+/// jobs such as selecting ui elements while keys or buttons are held.
+class Repeater : public Object {
+ public:
+ Repeater(seconds_t initial_delay, seconds_t repeat_delay, Runnable* runnable);
+ ~Repeater();
+
+ template
+ static auto New(seconds_t initial_delay, seconds_t repeat_delay,
+ const F& lambda) {
+ return Object::New(initial_delay, repeat_delay,
+ NewLambdaRunnable(lambda).Get());
+ }
+
+ private:
+ seconds_t initial_delay_;
+ seconds_t repeat_delay_;
+ Object::Ref timer_;
+ Object::Ref runnable_;
+};
+
+} // namespace ballistica::base
+
+#endif // BALLISTICA_BASE_SUPPORT_REPEATER_H_
diff --git a/src/ballistica/base/ui/dev_console.cc b/src/ballistica/base/ui/dev_console.cc
index 8b7dc05a..f0ac5365 100644
--- a/src/ballistica/base/ui/dev_console.cc
+++ b/src/ballistica/base/ui/dev_console.cc
@@ -2,6 +2,7 @@
#include "ballistica/base/ui/dev_console.h"
+#include "ballistica/base/app_adapter/app_adapter.h"
#include "ballistica/base/app_mode/app_mode.h"
#include "ballistica/base/audio/audio.h"
#include "ballistica/base/graphics/component/simple_component.h"
@@ -11,6 +12,7 @@
#include "ballistica/base/logic/logic.h"
#include "ballistica/base/platform/base_platform.h"
#include "ballistica/base/python/base_python.h"
+#include "ballistica/base/support/repeater.h"
#include "ballistica/base/ui/ui.h"
#include "ballistica/shared/foundation/event_loop.h"
#include "ballistica/shared/generic/utils.h"
@@ -20,14 +22,12 @@
namespace ballistica::base {
// How much of the screen the console covers when it is at full size.
-const float kDevConsoleSize = 0.9f;
-const int kDevConsoleLineLimit = 80;
-const int kDevConsoleStringBreakUpSize = 1950;
-const int kDevConsoleActivateKey1 = SDLK_BACKQUOTE;
-const int kDevConsoleActivateKey2 = SDLK_F2;
+const float kDevConsoleSize{0.9f};
+const int kDevConsoleLineLimit{80};
+const int kDevConsoleStringBreakUpSize{1950};
const float kDevConsoleTabButtonCornerRadius{16.0f};
-const double kTransitionSeconds = 0.15;
+const double kTransitionSeconds{0.15};
enum class DevConsoleHAnchor_ { kLeft, kCenter, kRight };
enum class DevButtonStyle_ { kNormal, kDark };
@@ -171,11 +171,7 @@ class DevConsole::Text_ : public DevConsole::Widget_ {
}
void Draw(RenderPass* pass, float bottom) override {
- Vector3f fgcolor;
- Vector3f bgcolor;
- fgcolor = Vector3f{0.8f, 0.7f, 0.8f};
- bgcolor = Vector3f{0.25, 0.2f, 0.3f};
-
+ auto fgcolor = Vector3f{0.8f, 0.7f, 0.8f};
DrawText(pass, &text_group, scale, bottom, x + XOffs(h_attach), y, fgcolor);
}
};
@@ -327,7 +323,6 @@ class DevConsole::ToggleButton_ : public DevConsole::Widget_ {
void Draw(RenderPass* pass, float bottom) override {
DrawRect(pass, &mesh, bottom, x + XOffs(attach), y, width, height,
-
pressed ? Vector3f{0.5f, 0.2f, 1.0f}
: on ? Vector3f{0.5f, 0.4f, 0.6f}
: Vector3f{0.25, 0.2f, 0.3f});
@@ -448,14 +443,11 @@ DevConsole::DevConsole() {
if (g_buildconfig.test_build()) {
title += " (test)";
}
-
title_text_group_.SetText(title);
built_text_group_.SetText("Built: " __DATE__ " " __TIME__);
prompt_text_group_.SetText(">");
}
-DevConsole::~DevConsole() = default;
-
void DevConsole::RefreshTabButtons_() {
// IMPORTANT: This code should always be run in its own top level call and
// never directly from user code. Otherwise we can wind up mucking with
@@ -695,22 +687,25 @@ void DevConsole::InputAdapterFinish() {
auto DevConsole::HandleKeyPress(const SDL_Keysym* keysym) -> bool {
assert(g_base->InLogicThread());
+ // Any presses or releases cancels repeat actions.
+ key_repeater_.Clear();
+
// Handle our toggle buttons no matter whether we're active.
- switch (keysym->sym) {
- case kDevConsoleActivateKey1:
- case kDevConsoleActivateKey2: {
- if (!g_buildconfig.demo_build() && !g_buildconfig.arcade_build()) {
- // (reset input so characters don't continue walking and stuff)
- g_base->input->ResetHoldStates();
- if (auto console = g_base->ui->dev_console()) {
- console->ToggleState();
- }
- }
- return true;
- }
- default:
- break;
- }
+ // switch (keysym->sym) {
+ // case kDevConsoleActivateKey1:
+ // case kDevConsoleActivateKey2: {
+ // if (!g_buildconfig.demo_build() && !g_buildconfig.arcade_build()) {
+ // // (reset input so characters don't continue walking and stuff)
+ // g_base->input->ResetHoldStates();
+ // if (auto console = g_base->ui->dev_console()) {
+ // console->ToggleState();
+ // }
+ // }
+ // return true;
+ // }
+ // default:
+ // break;
+ // }
if (state_ == State_::kInactive) {
return false;
@@ -725,18 +720,21 @@ auto DevConsole::HandleKeyPress(const SDL_Keysym* keysym) -> bool {
break;
}
- // Handle some stuff only with the Python terminal visible.
- if (python_terminal_visible_) {
+ // If we support direct keyboard input, and python terminal is showing,
+ // handle some keys directly.
+ if (python_terminal_visible_ && g_base->ui->UIHasDirectKeyboardInput()) {
switch (keysym->sym) {
- case SDLK_BACKSPACE:
- case SDLK_DELETE: {
- std::vector unichars =
- Utils::UnicodeFromUTF8(input_string_, "fjco38");
- if (!unichars.empty()) {
- unichars.resize(unichars.size() - 1);
- input_string_ = Utils::UTF8FromUnicode(unichars);
- input_text_dirty_ = true;
- }
+ case SDLK_BACKSPACE: {
+ key_repeater_ = Repeater::New(
+ g_base->app_adapter->GetKeyRepeatDelay(),
+ g_base->app_adapter->GetKeyRepeatInterval(), [this] {
+ auto unichars = Utils::UnicodeFromUTF8(input_string_, "fjco38");
+ if (!unichars.empty()) {
+ unichars.resize(unichars.size() - 1);
+ input_string_ = Utils::UTF8FromUnicode(unichars);
+ input_text_dirty_ = true;
+ }
+ });
break;
}
case SDLK_UP:
@@ -772,8 +770,20 @@ auto DevConsole::HandleKeyPress(const SDL_Keysym* keysym) -> bool {
break;
}
}
+ return true;
}
- return true;
+
+ // By default don't claim key events; we want to be able to show the
+ // console while still playing/navigating normally.
+ return false;
+}
+
+auto DevConsole::HandleKeyRelease(const SDL_Keysym* keysym) -> bool {
+ // Any presses or releases cancels repeat actions.
+ key_repeater_.Clear();
+
+ // Otherwise absorb *all* key-ups when we're active.
+ return state_ != State_::kInactive;
}
void DevConsole::Exec() {
@@ -879,17 +889,6 @@ auto DevConsole::HandleTextEditing(const std::string& text) -> bool {
return true;
}
-auto DevConsole::HandleKeyRelease(const SDL_Keysym* keysym) -> bool {
- // Always absorb our activate keys.
- if (keysym->sym == kDevConsoleActivateKey1
- || keysym->sym == kDevConsoleActivateKey2) {
- return true;
- }
-
- // Otherwise absorb *all* key-ups when we're active.
- return state_ != State_::kInactive;
-}
-
void DevConsole::Print(const std::string& s_in) {
assert(g_base->InLogicThread());
std::string s = Utils::GetValidUTF8(s_in.c_str(), "cspr");
diff --git a/src/ballistica/base/ui/dev_console.h b/src/ballistica/base/ui/dev_console.h
index c362ed6d..e7134c70 100644
--- a/src/ballistica/base/ui/dev_console.h
+++ b/src/ballistica/base/ui/dev_console.h
@@ -20,7 +20,6 @@ const float kDevConsoleZDepth = 0.0f;
class DevConsole {
public:
DevConsole();
- ~DevConsole();
auto IsActive() const -> bool { return (state_ != State_::kInactive); }
auto HandleTextEditing(const std::string& text) -> bool;
auto HandleKeyPress(const SDL_Keysym* keysym) -> bool;
@@ -82,18 +81,18 @@ class DevConsole {
void RefreshTabButtons_();
void RefreshTabContents_();
- bool input_text_dirty_{true};
- bool input_enabled_{};
- bool last_line_mesh_dirty_{true};
- bool python_terminal_visible_{};
- bool python_terminal_pressed_{};
- bool refresh_pending_{};
- int ui_lock_count_{};
+ int input_history_position_{};
+ int ui_lock_count_ : 1 {};
+ bool input_text_dirty_ : 1 {true};
+ bool input_enabled_ : 1 {};
+ bool last_line_mesh_dirty_ : 1 {true};
+ bool python_terminal_visible_ : 1 {};
+ bool python_terminal_pressed_ : 1 {};
+ bool refresh_pending_ : 1 {};
+ double transition_start_{};
State_ state_{State_::kInactive};
State_ state_prev_{State_::kInactive};
millisecs_t last_input_text_change_time_{};
- double transition_start_{};
- int input_history_position_{};
ImageMesh bg_mesh_;
ImageMesh stripe_mesh_;
ImageMesh border_mesh_;
@@ -103,15 +102,15 @@ class DevConsole {
TextGroup input_text_group_;
std::string last_line_;
std::string input_string_;
- std::list tabs_{"Python", "AppModes", "Logging", "Graphics",
- "UI"};
- std::string active_tab_{"Python"};
+ std::list tabs_;
+ std::string active_tab_;
PythonRef string_edit_adapter_;
- Object::Ref last_line_mesh_group_;
std::list input_history_;
std::list output_lines_;
std::vector > widgets_;
std::vector > tab_buttons_;
+ Object::Ref last_line_mesh_group_;
+ Object::Ref key_repeater_;
};
} // namespace ballistica::base
diff --git a/src/ballistica/base/ui/ui.cc b/src/ballistica/base/ui/ui.cc
index e9b50093..06930e4c 100644
--- a/src/ballistica/base/ui/ui.cc
+++ b/src/ballistica/base/ui/ui.cc
@@ -194,7 +194,7 @@ auto UI::UIHasDirectKeyboardInput() const -> bool {
// we'll probably want to pop up a controller-centric on-screen-keyboard
// thingie instead.
auto* ui_input_device = g_base->ui->GetUIInputDevice();
- if (auto* keyboard = g_base->ui->GetUIInputDevice()) {
+ if (auto* keyboard = g_base->input->keyboard_input()) {
if (ui_input_device == keyboard || ui_input_device == nullptr) {
return true;
}
@@ -532,7 +532,7 @@ void UI::OnAssetsAvailable() {
assert(g_base->InLogicThread());
// Spin up the dev console.
- if (!g_core->HeadlessMode()) {
+ if (!g_core->HeadlessMode() && !g_buildconfig.demo_build()) {
assert(dev_console_ == nullptr);
dev_console_ = new DevConsole();
diff --git a/src/ballistica/base/ui/ui.h b/src/ballistica/base/ui/ui.h
index 02cecbb7..7ed5dd92 100644
--- a/src/ballistica/base/ui/ui.h
+++ b/src/ballistica/base/ui/ui.h
@@ -17,6 +17,14 @@ class Widget;
namespace ballistica::base {
+/// Delay before moving through elements in the UI when a key/button/stick
+/// is held
+const seconds_t kUINavigationRepeatDelay{0.25};
+
+/// Interval after the initial delay when moving through UI elements when a
+/// key/button/stick is held.
+const seconds_t kUINavigationRepeatInterval{0.1};
+
// Our global UI subsystem. This acts as a manager/wrapper for individual UI
// feature-sets that provide specific UI functionality.
class UI {
@@ -84,10 +92,10 @@ class UI {
auto GetUIInputDevice() const -> InputDevice*;
/// Return true if there is a full desktop-style hardware keyboard
- /// attached and the active UI InputDevice is set to it or not set. This
+ /// attached and no non-keyboard device is currently controlling the UI. This
/// also may take language or user preferences into account. Editable text
/// elements can use this to opt in to accepting key events directly
- /// instead of popping up a string edit dialog.
+ /// instead of popping up string edit dialogs.
auto UIHasDirectKeyboardInput() const -> bool;
/// Schedule a back button press. Can be called from any thread.
diff --git a/src/ballistica/core/platform/apple/core_platform_apple.cc b/src/ballistica/core/platform/apple/core_platform_apple.cc
index 04d79880..c864f3ad 100644
--- a/src/ballistica/core/platform/apple/core_platform_apple.cc
+++ b/src/ballistica/core/platform/apple/core_platform_apple.cc
@@ -4,7 +4,6 @@
#include "ballistica/core/platform/apple/core_platform_apple.h"
#if BA_XCODE_BUILD
-#include
#include
#endif
@@ -12,6 +11,14 @@
#if BA_XCODE_BUILD
#include "ballistica/base/platform/apple/apple_utils.h"
+#include "ballistica/base/platform/apple/from_swift.h"
+#include "ballistica/shared/math/rect.h"
+#endif
+
+#if BA_XCODE_BUILD
+// This needs to be below ballistica headers since it relies on
+// some types in them but does not include headers itself.
+#include
#endif
namespace ballistica::core {
@@ -31,7 +38,7 @@ auto CorePlatformApple::GetDeviceV1AccountUUIDPrefix() -> std::string {
// Legacy for device-accounts; don't modify this code.
auto CorePlatformApple::GetRealLegacyDeviceUUID(std::string* uuid) -> bool {
#if BA_OSTYPE_MACOS && BA_XCODE_BUILD
- *uuid = base::AppleUtils::GetMacUUID();
+ *uuid = std::string(BallisticaKit::CocoaFromCpp::GetLegacyDeviceUUID());
return true;
#endif
#if BA_OSTYPE_IOS_TVOS
@@ -69,7 +76,8 @@ auto CorePlatformApple::GetDeviceUUIDInputs() -> std::list {
std::list out;
#if BA_OSTYPE_MACOS
#if BA_XCODE_BUILD
- out.push_back(base::AppleUtils::GetMacUUID());
+ out.push_back(
+ std::string(BallisticaKit::CocoaFromCpp::GetLegacyDeviceUUID()));
#else // BA_XCODE_BUILD
out.push_back(GetMacUUIDFallback());
#endif // BA_XCODE_BUILD
@@ -96,28 +104,13 @@ auto CorePlatformApple::DoGetConfigDirectoryMonolithicDefault()
printf("FIXME: get proper default-config-dir\n");
return std::string(getenv("HOME")) + "/Library";
#elif BA_OSTYPE_MACOS && BA_XCODE_BUILD
- return base::AppleUtils::GetApplicationSupportPath() + "/BallisticaKit";
+ return std::string(BallisticaKit::CocoaFromCpp::GetApplicationSupportPath())
+ + "/BallisticaKit";
#else
return CorePlatform::DoGetConfigDirectoryMonolithicDefault();
#endif
}
-auto CorePlatformApple::GetLocale() -> std::string {
-#if BA_XCODE_BUILD
- return BallisticaKit::FromCppGetLocaleString();
-#else
- return CorePlatform::GetLocale();
-#endif
-}
-
-auto CorePlatformApple::DoGetDeviceName() -> std::string {
-#if BA_OSTYPE_MACOS && BA_XCODE_BUILD
- return base::AppleUtils::GetDeviceName();
-#else
- return CorePlatform::DoGetDeviceName();
-#endif
-}
-
auto CorePlatformApple::DoHasTouchScreen() -> bool {
#if BA_OSTYPE_IOS
return true;
@@ -134,7 +127,7 @@ auto CorePlatformApple::GetDefaultUIScale() -> UIScale {
return UIScale::kSmall;
}
#else
- // Default case handles mac & tvos.
+ // The default case handles mac & tvos.
return CorePlatform::GetDefaultUIScale();
#endif
}
@@ -163,37 +156,37 @@ void CorePlatformApple::EmitPlatformLog(const std::string& name, LogLevel level,
auto CorePlatformApple::DoGetDataDirectoryMonolithicDefault() -> std::string {
#if BA_XCODE_BUILD
- return BallisticaKit::FromCppGetResourcesPath();
+ return BallisticaKit::FromCpp::GetResourcesPath();
#else
// Fall back to default.
return CorePlatform::DoGetDataDirectoryMonolithicDefault();
#endif
}
-void CorePlatformApple::GetTextBoundsAndWidth(const std::string& text, Rect* r,
- float* width) {
-#if BA_XCODE_BUILD && !BA_HEADLESS_BUILD
- base::AppleUtils::GetTextBoundsAndWidth(text, r, width);
-#else
- CorePlatform::GetTextBoundsAndWidth(text, r, width);
+#if BA_XCODE_BUILD
+class TextTextureWrapper_ {
+ public:
+ TextTextureWrapper_(int width, int height,
+ const std::vector& strings,
+ const std::vector& positions,
+ const std::vector& widths, float scale)
+ : data{BallisticaKit::TextTextureData::init(width, height, strings,
+ positions, widths, scale)} {}
+ BallisticaKit::TextTextureData data;
+};
#endif
-}
-
-void CorePlatformApple::FreeTextTexture(void* tex) {
-#if BA_XCODE_BUILD && !BA_HEADLESS_BUILD
- base::AppleUtils::FreeTextTexture(tex);
-#else
- CorePlatform::FreeTextTexture(tex);
-#endif
-}
auto CorePlatformApple::CreateTextTexture(
int width, int height, const std::vector& strings,
const std::vector& positions, const std::vector& widths,
float scale) -> void* {
#if BA_XCODE_BUILD && !BA_HEADLESS_BUILD
- return base::AppleUtils::CreateTextTexture(width, height, strings, positions,
- widths, scale);
+ auto* wrapper =
+ new TextTextureWrapper_(width, height, strings, positions, widths, scale);
+ // wrapper->old = base::AppleUtils::CreateTextTexture(width, height, strings,
+ // positions, widths,
+ // scale);
+ return wrapper;
#else
return CorePlatform::CreateTextTexture(width, height, strings, positions,
widths, scale);
@@ -202,12 +195,45 @@ auto CorePlatformApple::CreateTextTexture(
auto CorePlatformApple::GetTextTextureData(void* tex) -> uint8_t* {
#if BA_XCODE_BUILD && !BA_HEADLESS_BUILD
- return base::AppleUtils::GetTextTextureData(tex);
+ auto* wrapper = static_cast(tex);
+ return static_cast(wrapper->data.getTextTextureData());
+ // return base::AppleUtils::GetTextTextureData(wrapper->old);
#else
return CorePlatform::GetTextTextureData(tex);
#endif
}
+void CorePlatformApple::GetTextBoundsAndWidth(const std::string& text, Rect* r,
+ float* width) {
+#if BA_XCODE_BUILD && !BA_HEADLESS_BUILD
+
+ auto vals = BallisticaKit::TextTextureData::getTextBoundsAndWidth(text);
+ assert(vals.getCount() == 5);
+ r->l = vals[0];
+ r->r = vals[1];
+ r->b = vals[2];
+ r->t = vals[3];
+ *width = vals[4];
+
+// base::AppleUtils::GetTextBoundsAndWidth(text, r, width);
+// printf("GOT BOUNDS l=%.2f r=%.2f b=%.2f t=%.2f w=%.2f\n", r->l, r->r, r->b,
+// r->t, *width); printf("SWIFT BOUNDS l=%.2f r=%.2f b=%.2f t=%.2f w=%.2f\n",
+// vals[0], vals[1], vals[2], vals[3], vals[4]);
+#else
+ CorePlatform::GetTextBoundsAndWidth(text, r, width);
+#endif
+}
+
+void CorePlatformApple::FreeTextTexture(void* tex) {
+#if BA_XCODE_BUILD && !BA_HEADLESS_BUILD
+ auto* wrapper = static_cast(tex);
+ // base::AppleUtils::FreeTextTexture(wrapper->old);
+ delete wrapper;
+#else
+ CorePlatform::FreeTextTexture(tex);
+#endif
+}
+
void CorePlatformApple::SubmitScore(const std::string& game,
const std::string& version, int64_t score) {
#if BA_USE_GAME_CENTER
@@ -278,15 +304,18 @@ void CorePlatformApple::GameCenterLogin() {
auto CorePlatformApple::IsOSPlayingMusic() -> bool {
#if BA_XCODE_BUILD
- return base::AppleUtils::IsMusicPlaying();
+ // FIXME - should look into doing this properly these days, or whether
+ // this is still needed at all.
+ return false;
+ // return base::AppleUtils::IsMusicPlaying();
#else
return CorePlatform::IsOSPlayingMusic();
#endif
}
void CorePlatformApple::OpenFileExternally(const std::string& path) {
-#if BA_XCODE_BUILD
- base::AppleUtils::EditTextFile(path.c_str());
+#if BA_OSTYPE_MACOS && BA_XCODE_BUILD
+ BallisticaKit::CocoaFromCpp::OpenFileExternally(path);
#else
CorePlatform::OpenFileExternally(path);
#endif
@@ -294,7 +323,7 @@ void CorePlatformApple::OpenFileExternally(const std::string& path) {
void CorePlatformApple::OpenDirExternally(const std::string& path) {
#if BA_OSTYPE_MACOS && BA_XCODE_BUILD
- BallisticaKit::CocoaFromCppOpenDirExternally(path);
+ BallisticaKit::CocoaFromCpp::OpenDirExternally(path);
#else
CorePlatform::OpenDirExternally(path);
#endif
diff --git a/src/ballistica/core/platform/apple/core_platform_apple.h b/src/ballistica/core/platform/apple/core_platform_apple.h
index 6419215d..93923c0a 100644
--- a/src/ballistica/core/platform/apple/core_platform_apple.h
+++ b/src/ballistica/core/platform/apple/core_platform_apple.h
@@ -21,8 +21,8 @@ class CorePlatformApple : public CorePlatform {
auto GenerateUUID() -> std::string override;
auto DoGetConfigDirectoryMonolithicDefault()
-> std::optional override;
- auto GetLocale() -> std::string override;
- auto DoGetDeviceName() -> std::string override;
+ // auto GetLocale() -> std::string override;
+ // auto DoGetDeviceName() -> std::string override;
auto DoHasTouchScreen() -> bool override;
auto GetDefaultUIScale() -> UIScale override;
auto IsRunningOnDesktop() -> bool override;
diff --git a/src/ballistica/core/platform/support/min_sdl.h b/src/ballistica/core/platform/support/min_sdl.h
index b827e46d..e20e4010 100644
--- a/src/ballistica/core/platform/support/min_sdl.h
+++ b/src/ballistica/core/platform/support/min_sdl.h
@@ -328,7 +328,7 @@ typedef enum {
#define SDLK_SCANCODE_MASK (1 << 30)
#define SDL_SCANCODE_TO_KEYCODE(X) (X | SDLK_SCANCODE_MASK)
-enum {
+enum SDL_KeycodeEnum {
SDLK_UNKNOWN = 0,
SDLK_RETURN = '\r',
diff --git a/src/ballistica/scene_v1/python/class/python_class_base_timer.cc b/src/ballistica/scene_v1/python/class/python_class_base_timer.cc
index 0e095b7a..faa8f8d6 100644
--- a/src/ballistica/scene_v1/python/class/python_class_base_timer.cc
+++ b/src/ballistica/scene_v1/python/class/python_class_base_timer.cc
@@ -94,7 +94,7 @@ auto PythonClassBaseTimer::tp_new(PyTypeObject* type, PyObject* args,
self->timer_id_ = SceneV1Context::Current().NewTimer(
TimeType::kBase, static_cast(length * 1000.0),
static_cast(repeat),
- Object::New(call_obj));
+ Object::New(call_obj).Get());
self->have_timer_ = true;
return reinterpret_cast(self);
diff --git a/src/ballistica/scene_v1/python/class/python_class_scene_timer.cc b/src/ballistica/scene_v1/python/class/python_class_scene_timer.cc
index e505b6ad..f9a8c608 100644
--- a/src/ballistica/scene_v1/python/class/python_class_scene_timer.cc
+++ b/src/ballistica/scene_v1/python/class/python_class_scene_timer.cc
@@ -100,7 +100,7 @@ auto PythonClassSceneTimer::tp_new(PyTypeObject* type, PyObject* args,
self->timer_id_ = SceneV1Context::Current().NewTimer(
TimeType::kSim, static_cast(length * 1000.0),
static_cast(repeat),
- Object::New(call_obj));
+ Object::New(call_obj).Get());
self->have_timer_ = true;
return reinterpret_cast(self);
diff --git a/src/ballistica/scene_v1/python/methods/python_methods_scene.cc b/src/ballistica/scene_v1/python/methods/python_methods_scene.cc
index 77b887bb..ab7e2e73 100644
--- a/src/ballistica/scene_v1/python/methods/python_methods_scene.cc
+++ b/src/ballistica/scene_v1/python/methods/python_methods_scene.cc
@@ -98,7 +98,7 @@ static auto PyTimer(PyObject* self, PyObject* args, PyObject* keywds)
SceneV1Context::Current().NewTimer(
TimeType::kSim, static_cast(length * 1000.0),
static_cast(repeat),
- Object::New(call_obj));
+ Object::New(call_obj).Get());
Py_RETURN_NONE;
BA_PYTHON_CATCH;
@@ -207,7 +207,7 @@ static auto PyBaseTimer(PyObject* self, PyObject* args, PyObject* keywds)
SceneV1Context::Current().NewTimer(
TimeType::kBase, static_cast(length * 1000.0),
static_cast(repeat),
- Object::New(call_obj));
+ Object::New(call_obj).Get());
Py_RETURN_NONE;
BA_PYTHON_CATCH;
diff --git a/src/ballistica/scene_v1/support/host_activity.cc b/src/ballistica/scene_v1/support/host_activity.cc
index b357fbb7..17fe55bd 100644
--- a/src/ballistica/scene_v1/support/host_activity.cc
+++ b/src/ballistica/scene_v1/support/host_activity.cc
@@ -178,7 +178,7 @@ void HostActivity::Start() {
// Create our step timer - gets called whenever scene should step.
step_scene_timer_id_ =
host_session->NewTimer(TimeType::kBase, kGameStepMilliseconds, true,
- NewLambdaRunnable([this] { StepScene(); }));
+ NewLambdaRunnable([this] { StepScene(); }).Get());
session_base_timer_ids_.push_back(step_scene_timer_id_);
UpdateStepTimerLength();
}
@@ -363,7 +363,7 @@ auto HostActivity::globals_node() const -> GlobalsNode* {
}
auto HostActivity::NewSimTimer(millisecs_t length, bool repeat,
- const Object::Ref& runnable) -> int {
+ Runnable* runnable) -> int {
if (shutting_down_) {
BA_LOG_PYTHON_TRACE_ONCE(
"WARNING: Creating game timer during host-activity shutdown");
@@ -384,7 +384,7 @@ auto HostActivity::NewSimTimer(millisecs_t length, bool repeat,
}
auto HostActivity::NewBaseTimer(millisecs_t length, bool repeat,
- const Object::Ref& runnable) -> int {
+ Runnable* runnable) -> int {
if (shutting_down_) {
BA_LOG_PYTHON_TRACE_ONCE(
"WARNING: Creating session-time timer during host-activity shutdown");
@@ -543,10 +543,10 @@ void HostActivity::DumpFullState(SessionStream* out) {
}
auto HostActivity::NewTimer(TimeType timetype, TimerMedium length, bool repeat,
- const Object::Ref& runnable) -> int {
+ Runnable* runnable) -> int {
// Make sure the runnable passed in is reference-managed already.
// (we may not add an initial reference ourself)
- assert(runnable.IsValidManagedObject());
+ assert(Object::IsValidManagedObject(runnable));
// We currently support game and base timers.
switch (timetype) {
diff --git a/src/ballistica/scene_v1/support/host_activity.h b/src/ballistica/scene_v1/support/host_activity.h
index 5f583ff9..507e75d0 100644
--- a/src/ballistica/scene_v1/support/host_activity.h
+++ b/src/ballistica/scene_v1/support/host_activity.h
@@ -25,7 +25,7 @@ class HostActivity : public SceneV1Context {
// ContextTarget time/timer support.
auto NewTimer(TimeType timetype, TimerMedium length, bool repeat,
- const Object::Ref& runnable) -> int override;
+ Runnable* runnable) -> int override;
void DeleteTimer(TimeType timetype, int timer_id) override;
auto GetTime(TimeType timetype) -> millisecs_t override;
@@ -77,11 +77,9 @@ class HostActivity : public SceneV1Context {
private:
void HandleOutOfBoundsNodes();
- auto NewSimTimer(millisecs_t length, bool repeat,
- const Object::Ref& runnable) -> int;
+ auto NewSimTimer(millisecs_t length, bool repeat, Runnable* runnable) -> int;
void DeleteSimTimer(int timer_id);
- auto NewBaseTimer(millisecs_t length, bool repeat,
- const Object::Ref& runnable) -> int;
+ auto NewBaseTimer(millisecs_t length, bool repeat, Runnable* runnable) -> int;
void DeleteBaseTimer(int timer_id);
void UpdateStepTimerLength();
void StepScene();
diff --git a/src/ballistica/scene_v1/support/host_session.cc b/src/ballistica/scene_v1/support/host_session.cc
index a5d04506..9c726733 100644
--- a/src/ballistica/scene_v1/support/host_session.cc
+++ b/src/ballistica/scene_v1/support/host_session.cc
@@ -40,7 +40,7 @@ HostSession::HostSession(PyObject* session_type_obj)
// Create a timer to step our session scene.
step_scene_timer_ =
base_timers_.NewTimer(base_time_millisecs_, kGameStepMilliseconds, 0, -1,
- NewLambdaRunnable([this] { StepScene(); }));
+ NewLambdaRunnable([this] { StepScene(); }).Get());
// Set up our output-stream, which will go to a replay and/or the network.
// We don't dump to a replay if we're doing the main menu; that replay
@@ -766,8 +766,8 @@ void HostSession::GetCorrectionMessages(
}
auto HostSession::NewTimer(TimeType timetype, TimerMedium length, bool repeat,
- const Object::Ref& runnable) -> int {
- assert(runnable.IsValidManagedObject());
+ Runnable* runnable) -> int {
+ assert(Object::IsValidManagedObject(runnable));
// We currently support game and base timers.
switch (timetype) {
diff --git a/src/ballistica/scene_v1/support/host_session.h b/src/ballistica/scene_v1/support/host_session.h
index d17de58d..cf53cccf 100644
--- a/src/ballistica/scene_v1/support/host_session.h
+++ b/src/ballistica/scene_v1/support/host_session.h
@@ -38,7 +38,7 @@ class HostSession : public Session {
// ContextTarget time/timer support
auto NewTimer(TimeType timetype, TimerMedium length, bool repeat,
- const Object::Ref& runnable) -> int override;
+ Runnable* runnable) -> int override;
void DeleteTimer(TimeType timetype, int timer_id) override;
auto GetTime(TimeType timetype) -> millisecs_t override;
diff --git a/src/ballistica/scene_v1/support/scene_v1_context.cc b/src/ballistica/scene_v1/support/scene_v1_context.cc
index d4f8f9c5..fa3becf2 100644
--- a/src/ballistica/scene_v1/support/scene_v1_context.cc
+++ b/src/ballistica/scene_v1/support/scene_v1_context.cc
@@ -55,12 +55,10 @@ auto SceneV1Context::GetAsHostActivity() -> HostActivity* { return nullptr; }
auto SceneV1Context::GetMutableScene() -> Scene* { return nullptr; }
auto SceneV1Context::NewTimer(TimeType timetype, TimerMedium length,
- bool repeat,
- const Object::Ref& runnable) -> int {
+ bool repeat, Runnable* runnable) -> int {
// Make sure the passed runnable has a ref-count already
// (don't want them to rely on us to create initial one).
- assert(runnable.Exists());
- assert(Object::IsValidManagedObject(runnable.Get()));
+ assert(Object::IsValidManagedObject(runnable));
switch (timetype) {
case TimeType::kSim:
diff --git a/src/ballistica/scene_v1/support/scene_v1_context.h b/src/ballistica/scene_v1/support/scene_v1_context.h
index 16414ecd..60e27916 100644
--- a/src/ballistica/scene_v1/support/scene_v1_context.h
+++ b/src/ballistica/scene_v1/support/scene_v1_context.h
@@ -61,7 +61,7 @@ class SceneV1Context : public base::Context {
// Default NewTimer implementation throws a descriptive error, so it can
// be useful to fall back on for unsupported cases.
virtual auto NewTimer(TimeType timetype, TimerMedium length, bool repeat,
- const Object::Ref& runnable) -> int;
+ Runnable* runnable) -> int;
virtual void DeleteTimer(TimeType timetype, int timer_id);
virtual auto GetTexture(const std::string& name) -> Object::Ref;
diff --git a/src/ballistica/shared/ballistica.cc b/src/ballistica/shared/ballistica.cc
index 8a23ce50..747e6a9a 100644
--- a/src/ballistica/shared/ballistica.cc
+++ b/src/ballistica/shared/ballistica.cc
@@ -39,7 +39,7 @@ auto main(int argc, char** argv) -> int {
namespace ballistica {
// These are set automatically via script; don't modify them here.
-const int kEngineBuildNumber = 21491;
+const int kEngineBuildNumber = 21510;
const char* kEngineVersion = "1.7.28";
const int kEngineApiVersion = 8;
diff --git a/src/ballistica/shared/foundation/event_loop.cc b/src/ballistica/shared/foundation/event_loop.cc
index 9133557b..f6e9850d 100644
--- a/src/ballistica/shared/foundation/event_loop.cc
+++ b/src/ballistica/shared/foundation/event_loop.cc
@@ -607,11 +607,11 @@ auto EventLoop::AreEventLoopsSuspended() -> bool {
return g_core->event_loops_suspended;
}
-auto EventLoop::NewTimer(millisecs_t length, bool repeat,
- const Object::Ref& runnable) -> Timer* {
+auto EventLoop::NewTimer(millisecs_t length, bool repeat, Runnable* runnable)
+ -> Timer* {
assert(g_core);
assert(ThreadIsCurrent());
- assert(runnable.Exists());
+ assert(Object::IsValidManagedObject(runnable));
return timers_.NewTimer(g_core->GetAppTimeMillisecs(), length, 0,
repeat ? -1 : 0, runnable);
}
diff --git a/src/ballistica/shared/foundation/event_loop.h b/src/ballistica/shared/foundation/event_loop.h
index 40f03631..2551b00a 100644
--- a/src/ballistica/shared/foundation/event_loop.h
+++ b/src/ballistica/shared/foundation/event_loop.h
@@ -56,8 +56,7 @@ class EventLoop {
auto identifier() const -> EventLoopID { return identifier_; }
// Register a timer to run on the thread.
- auto NewTimer(millisecs_t length, bool repeat,
- const Object::Ref& runnable) -> Timer*;
+ auto NewTimer(millisecs_t length, bool repeat, Runnable* runnable) -> Timer*;
Timer* GetTimer(int id);
void DeleteTimer(int id);
diff --git a/src/ballistica/shared/generic/timer_list.cc b/src/ballistica/shared/generic/timer_list.cc
index 84dd97f5..7bcf8b27 100644
--- a/src/ballistica/shared/generic/timer_list.cc
+++ b/src/ballistica/shared/generic/timer_list.cc
@@ -160,10 +160,11 @@ auto TimerList::GetExpiredTimer(TimerMedium target_time) -> Timer* {
auto TimerList::NewTimer(TimerMedium current_time, TimerMedium length,
TimerMedium offset, int repeat_count,
- const Object::Ref& runnable) -> Timer* {
+ Runnable* runnable) -> Timer* {
assert(!are_clearing_);
auto* t = new Timer(this, next_timer_id_++, current_time, length, offset,
repeat_count);
+ assert(Object::IsValidManagedObject(runnable));
t->runnable_ = runnable;
// Clion (correctly) points out that t may get deallocated in this call,
diff --git a/src/ballistica/shared/generic/timer_list.h b/src/ballistica/shared/generic/timer_list.h
index 6c0957e2..a954b759 100644
--- a/src/ballistica/shared/generic/timer_list.h
+++ b/src/ballistica/shared/generic/timer_list.h
@@ -22,8 +22,8 @@ class TimerList {
// Create a timer with provided runnable.
auto NewTimer(TimerMedium current_time, TimerMedium length,
- TimerMedium offset, int repeat_count,
- const Object::Ref& runnable) -> Timer*;
+ TimerMedium offset, int repeat_count, Runnable* runnable)
+ -> Timer*;
// Return a timer by its id, or nullptr if the timer no longer exists.
auto GetTimer(int id) -> Timer*;
diff --git a/src/ballistica/ui_v1/widget/button_widget.cc b/src/ballistica/ui_v1/widget/button_widget.cc
index 25300269..e8ce6a37 100644
--- a/src/ballistica/ui_v1/widget/button_widget.cc
+++ b/src/ballistica/ui_v1/widget/button_widget.cc
@@ -485,8 +485,8 @@ auto ButtonWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
pressed_ = true;
if (repeat_) {
- repeat_timer_ =
- base::NewAppTimer(300, true, [this] { OnRepeatTimerExpired(); });
+ repeat_timer_ = base::AppTimer::New(
+ 300, true, [this] { OnRepeatTimerExpired(); });
// If we're a repeat button we trigger immediately.
// (waiting till mouse up sort of defeats the purpose here)
diff --git a/src/ballistica/ui_v1/widget/h_scroll_widget.cc b/src/ballistica/ui_v1/widget/h_scroll_widget.cc
index d94c89f8..c8d23371 100644
--- a/src/ballistica/ui_v1/widget/h_scroll_widget.cc
+++ b/src/ballistica/ui_v1/widget/h_scroll_widget.cc
@@ -405,7 +405,7 @@ auto HScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
// Top level touches eventually get passed as mouse-downs if no
// scrolling has started.
if (static_cast(m.type)) {
- touch_delay_timer_ = base::NewAppTimer(
+ touch_delay_timer_ = base::AppTimer::New(
150, false, [this] { OnTouchDelayTimerExpired(); });
}
diff --git a/src/ballistica/ui_v1/widget/scroll_widget.cc b/src/ballistica/ui_v1/widget/scroll_widget.cc
index 6e83222f..de48022c 100644
--- a/src/ballistica/ui_v1/widget/scroll_widget.cc
+++ b/src/ballistica/ui_v1/widget/scroll_widget.cc
@@ -330,7 +330,7 @@ auto ScrollWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
// After a short delay we go ahead and handle this as a regular
// click if it hasn't turned into a scroll or a child scroll.
if (!child_is_scrolling_) {
- touch_delay_timer_ = base::NewAppTimer(
+ touch_delay_timer_ = base::AppTimer::New(
150, false, [this] { OnTouchDelayTimerExpired(); });
}
}
diff --git a/src/ballistica/ui_v1/widget/text_widget.cc b/src/ballistica/ui_v1/widget/text_widget.cc
index edeeeaea..f6957b82 100644
--- a/src/ballistica/ui_v1/widget/text_widget.cc
+++ b/src/ballistica/ui_v1/widget/text_widget.cc
@@ -659,7 +659,6 @@ auto TextWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
if (m.has_keysym && !ShouldUseStringEditor_()) {
last_carat_change_time_millisecs_ =
static_cast(g_base->logic->display_time() * 1000.0);
-
text_group_dirty_ = true;
bool claimed = false;
switch (m.keysym.sym) {
@@ -724,112 +723,29 @@ auto TextWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
break;
}
if (!claimed) {
- // Pop in a char.
- if (editable()) {
- claimed = true;
-
- // #if BA_SDL2_BUILD || BA_MINSDL_BUILD
- // // On SDL2, chars come through as TEXT_INPUT messages;
- // // can ignore this.
- // #else
- // std::vector unichars =
- // Utils::UnicodeFromUTF8(text_raw_, "2jf987");
- // int len = static_cast(unichars.size());
-
- // if (len < max_chars_) {
- // if ((m.keysym.unicode >= 32) && (m.keysym.sym != SDLK_TAB))
- // {
- // claimed = true;
- // int pos = carat_position_;
- // if (pos > len) pos = len;
- // unichars.insert(unichars.begin() + pos,
- // m.keysym.unicode); text_raw_ =
- // Utils::UTF8FromUnicode(unichars); text_translation_dirty_
- // = true; carat_position_++;
- // } else {
- // // These don't seem to come through cleanly as unicode:
- // // FIXME - should re-check this on SDL2 builds
-
- // claimed = true;
- // std::string s;
- // uint32_t pos = carat_position_;
- // if (pos > len) pos = len;
- // switch (m.keysym.sym) {
- // case SDLK_KP0:
- // s = '0';
- // break;
- // case SDLK_KP1:
- // s = '1';
- // break;
- // case SDLK_KP2:
- // s = '2';
- // break;
- // case SDLK_KP3:
- // s = '3';
- // break;
- // case SDLK_KP4:
- // s = '4';
- // break;
- // case SDLK_KP5:
- // s = '5';
- // break;
- // case SDLK_KP6:
- // s = '6';
- // break;
- // case SDLK_KP7:
- // s = '7';
- // break;
- // case SDLK_KP8:
- // s = '8';
- // break;
- // case SDLK_KP9:
- // s = '9';
- // break;
- // case SDLK_KP_PERIOD:
- // s = '.';
- // break;
- // case SDLK_KP_DIVIDE:
- // s = '/';
- // break;
- // case SDLK_KP_MULTIPLY:
- // s = '*';
- // break;
- // case SDLK_KP_MINUS:
- // s = '-';
- // break;
- // case SDLK_KP_PLUS:
- // s = '+';
- // break;
- // case SDLK_KP_EQUALS:
- // s = '=';
- // break;
- // default:
- // break;
- // }
- // if (s.size() > 0) {
- // unichars.insert(unichars.begin() + pos, s[0]);
- // text_raw_ = Utils::UTF8FromUnicode(unichars);
- // text_translation_dirty_ = true;
- // carat_position_++;
- // }
- // }
- // }
- // #endif // BA_SDL2_BUILD
- }
+ // Direct text edits come through as seperate events, but we still
+ // want to claim key down events here; otherwise they'll do weird
+ // stuff like navigate to other widgets.
+ claimed = true;
}
return claimed;
}
switch (m.type) {
case base::WidgetMessage::Type::kTextInput: {
- // If we're using an edit dialog, any attempted text input just kicks us
- // over to that.
- if (editable() && ShouldUseStringEditor_()) {
- InvokeStringEditor_();
- } else {
- // Otherwise apply the text directly.
- if (editable() && m.sval != nullptr) {
- AddCharsToText_(*m.sval);
- return true;
+ if (editable()) {
+ if (ShouldUseStringEditor_()) {
+ // Normally we shouldn't be getting direct text input events in
+ // situations where we're using string editors, but it still might
+ // be possible; for instance if a game controller is driving the
+ // ui when a key is typed. We simply ignore the event in that case
+ // because otherwise the text input would be fighting with the
+ // string-editor.
+ } else {
+ // Apply text directly.
+ if (m.sval != nullptr) {
+ AddCharsToText_(*m.sval);
+ return true;
+ }
}
}
break;