more apple version work and cleaning up keyboard input

This commit is contained in:
Eric 2023-10-26 10:11:20 -07:00
parent a2ff9ab872
commit 2ec08d706e
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
66 changed files with 1112 additions and 1055 deletions

88
.efrocachemap generated
View File

@ -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",

View File

@ -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)

View File

@ -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

View File

@ -434,6 +434,8 @@
<ClCompile Include="..\..\src\ballistica\base\support\huffman.cc" />
<ClInclude Include="..\..\src\ballistica\base\support\huffman.h" />
<ClInclude Include="..\..\src\ballistica\base\support\plus_soft.h" />
<ClCompile Include="..\..\src\ballistica\base\support\repeater.cc" />
<ClInclude Include="..\..\src\ballistica\base\support\repeater.h" />
<ClCompile Include="..\..\src\ballistica\base\support\stdio_console.cc" />
<ClInclude Include="..\..\src\ballistica\base\support\stdio_console.h" />
<ClCompile Include="..\..\src\ballistica\base\support\stress_test.cc" />

View File

@ -736,6 +736,12 @@
<ClInclude Include="..\..\src\ballistica\base\support\plus_soft.h">
<Filter>ballistica\base\support</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\support\repeater.cc">
<Filter>ballistica\base\support</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\base\support\repeater.h">
<Filter>ballistica\base\support</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\support\stdio_console.cc">
<Filter>ballistica\base\support</Filter>
</ClCompile>

View File

@ -429,6 +429,8 @@
<ClCompile Include="..\..\src\ballistica\base\support\huffman.cc" />
<ClInclude Include="..\..\src\ballistica\base\support\huffman.h" />
<ClInclude Include="..\..\src\ballistica\base\support\plus_soft.h" />
<ClCompile Include="..\..\src\ballistica\base\support\repeater.cc" />
<ClInclude Include="..\..\src\ballistica\base\support\repeater.h" />
<ClCompile Include="..\..\src\ballistica\base\support\stdio_console.cc" />
<ClInclude Include="..\..\src\ballistica\base\support\stdio_console.h" />
<ClCompile Include="..\..\src\ballistica\base\support\stress_test.cc" />

View File

@ -736,6 +736,12 @@
<ClInclude Include="..\..\src\ballistica\base\support\plus_soft.h">
<Filter>ballistica\base\support</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\support\repeater.cc">
<Filter>ballistica\base\support</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\base\support\repeater.h">
<Filter>ballistica\base\support</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\base\support\stdio_console.cc">
<Filter>ballistica\base\support</Filter>
</ClCompile>

View File

@ -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:

View File

@ -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'

View File

@ -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

View File

@ -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();

View File

@ -3,16 +3,21 @@
#include "ballistica/base/app_adapter/app_adapter_apple.h"
#include <BallisticaKit-Swift.h>
#include "ballistica/base/graphics/gl/renderer_gl.h"
#include "ballistica/base/graphics/graphics.h"
#include "ballistica/base/graphics/graphics_server.h"
#include "ballistica/base/logic/logic.h"
#include "ballistica/base/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 <BallisticaKit-Swift.h>
// 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

View File

@ -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;

View File

@ -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;
}

View File

@ -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>* asset_ref_ptr) {

View File

@ -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<uint8_t*>(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;

View File

@ -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

View File

@ -99,6 +99,7 @@ class RenderPass;
class RenderTarget;
class RemoteAppServer;
class RemoteControlInput;
class Repeater;
class ScoreToBeat;
class AppAdapterSDL;
class SDLContext;

View File

@ -182,7 +182,7 @@ void Graphics::UpdateInitialGraphicsSettingsSend_() {
void Graphics::StepDisplayTime() { assert(g_base->InLogicThread()); }
void Graphics::AddCleanFrameCommand(const Object::Ref<PythonContextCall>& 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());

View File

@ -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<ImageMesh> screen_mesh_;
Object::Ref<ImageMesh> progress_bar_bottom_mesh_;
Object::Ref<ImageMesh> progress_bar_top_mesh_;

View File

@ -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<int>(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

View File

@ -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<TextureCompressionType>& 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<GraphicsClientContext>* 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_{};

View File

@ -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<TextPacker::Span> spans;
packer->compile();
packer->Compile();
// DEBUGGING - add a single quad above our first
// span showing the entire texture for debugging purposes

View File

@ -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,

View File

@ -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<float>(x + 1) + x_offs + scale_extra;
g.tex_max_y = (1.0f / 8.0f) * static_cast<float>(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<int>(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<int>(FontPage::kExtras1): {
@ -887,52 +886,57 @@ void TextGraphics::GetFontPagesForText(const std::string& text,
int last_page = -1;
std::vector<uint32_t> 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+E000U+F8FF) we point at our own
// custom page(s)
bool covered{};
// For values in the custom-char range (U+E000U+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<int>(FontPage::kExtras1);
covered = true;
} else if (val < 0xE000 + 50) {
// Special value denoting our custom font page.
page = static_cast<int>(FontPage::kExtras2);
covered = true;
} else if (val < 0xE000 + 75) {
// Special value denoting our custom font page.
page = static_cast<int>(FontPage::kExtras3);
covered = true;
} else if (val < 0xE000 + 100) {
// Special value denoting our custom font page.
page = static_cast<int>(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<int>(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<TextSpanBoundsCacheEntry>());
@ -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);

View File

@ -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];

View File

@ -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<Span>& { 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<Span> spans_;
};

View File

@ -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<millisecs_t>(static_cast<float>(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<int>(e->jaxis.axis),
static_cast<int>(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;
}

View File

@ -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<int> 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<int> run_buttons_held_;
std::string custom_device_name_;
std::string raw_sdl_joystick_name_;
std::string raw_sdl_joystick_identifier_;
Object::Ref<Repeater> ui_repeater_;
BA_DISALLOW_CLASS_COPIES(JoystickInput);
};

View File

@ -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"; }

View File

@ -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<int> keys_held_;
Object::Ref<Repeater> ui_repeater_;
};
} // namespace ballistica::base

View File

@ -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);
}
}

View File

@ -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>& 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();
}

View File

@ -89,8 +89,7 @@ class Logic {
void HandleInterruptSignal();
void HandleTerminateSignal();
auto NewAppTimer(millisecs_t length, bool repeat,
const Object::Ref<Runnable>& 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);

View File

@ -4,11 +4,14 @@
#include "ballistica/base/platform/apple/base_platform_apple.h"
#if BA_XCODE_BUILD
#include <BallisticaKit-Swift.h>
#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 <BallisticaKit-Swift.h>
#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

View File

@ -95,7 +95,7 @@ auto PythonClassAppTimer::tp_new(PyTypeObject* type, PyObject* args,
self->timer_id_ = g_base->logic->NewAppTimer(
static_cast<millisecs_t>(length * 1000.0), repeat,
Object::New<Runnable, PythonContextCallRunnable>(call_obj));
Object::New<Runnable, PythonContextCallRunnable>(call_obj).Get());
self->have_timer_ = true;

View File

@ -366,7 +366,7 @@ static auto PyAppTimer(PyObject* self, PyObject* args, PyObject* keywds)
}
g_base->logic->NewAppTimer(
static_cast<millisecs_t>(length * 1000.0), false,
Object::New<Runnable, PythonContextCallRunnable>(call_obj));
Object::New<Runnable, PythonContextCallRunnable>(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<PyMethodDef> {
@ -1702,6 +1748,8 @@ auto PythonMethodsApp::GetMethods() -> std::vector<PyMethodDef> {
PyDevConsoleInputAdapterFinishDef,
PyAudioShutdownBeginDef,
PyAudioShutdownIsCompleteDef,
PyGraphicsShutdownBeginDef,
PyGraphicsShutdownIsCompleteDef,
};
}

View File

@ -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<bool>(fade),
static_cast<int>(1000.0f * time), endcall);
Py_RETURN_NONE;

View File

@ -13,12 +13,17 @@ namespace ballistica::base {
class AppTimer : public Object {
public:
AppTimer(millisecs_t length, bool repeat,
const Object::Ref<Runnable>& runnable) {
AppTimer(millisecs_t length, bool repeat, Runnable* runnable) {
assert(g_base->InLogicThread());
timer_id_ = base::g_base->logic->NewAppTimer(length, repeat, runnable);
}
template <typename F>
static auto New(millisecs_t length, bool repeat, const F& lambda) {
return Object::New<AppTimer>(length, repeat,
NewLambdaRunnable<F>(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 <typename F>
auto NewAppTimer(millisecs_t length, bool repeat, const F& lambda)
-> Object::Ref<AppTimer> {
return Object::New<AppTimer>(length, repeat, NewLambdaRunnable<F>(lambda));
}
} // namespace ballistica::base
#endif // BALLISTICA_BASE_SUPPORT_APP_TIMER_H_

View File

@ -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<Repeater>(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<millisecs_t>(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<millisecs_t>(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

View File

@ -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 <typename F>
static auto New(seconds_t initial_delay, seconds_t repeat_delay,
const F& lambda) {
return Object::New<Repeater>(initial_delay, repeat_delay,
NewLambdaRunnable<F>(lambda).Get());
}
private:
seconds_t initial_delay_;
seconds_t repeat_delay_;
Object::Ref<AppTimer> timer_;
Object::Ref<Runnable> runnable_;
};
} // namespace ballistica::base
#endif // BALLISTICA_BASE_SUPPORT_REPEATER_H_

View File

@ -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<uint32_t> 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");

View File

@ -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<std::string> tabs_{"Python", "AppModes", "Logging", "Graphics",
"UI"};
std::string active_tab_{"Python"};
std::list<std::string> tabs_;
std::string active_tab_;
PythonRef string_edit_adapter_;
Object::Ref<TextGroup> last_line_mesh_group_;
std::list<std::string> input_history_;
std::list<OutputLine_> output_lines_;
std::vector<std::unique_ptr<Widget_> > widgets_;
std::vector<std::unique_ptr<Widget_> > tab_buttons_;
Object::Ref<TextGroup> last_line_mesh_group_;
Object::Ref<Repeater> key_repeater_;
};
} // namespace ballistica::base

View File

@ -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();

View File

@ -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.

View File

@ -4,7 +4,6 @@
#include "ballistica/core/platform/apple/core_platform_apple.h"
#if BA_XCODE_BUILD
#include <BallisticaKit-Swift.h>
#include <unistd.h>
#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 <BallisticaKit-Swift.h>
#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::string> {
std::list<std::string> 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<std::string>& strings,
const std::vector<float>& positions,
const std::vector<float>& 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<std::string>& strings,
const std::vector<float>& positions, const std::vector<float>& 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<TextTextureWrapper_*>(tex);
return static_cast<uint8_t*>(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<TextTextureWrapper_*>(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

View File

@ -21,8 +21,8 @@ class CorePlatformApple : public CorePlatform {
auto GenerateUUID() -> std::string override;
auto DoGetConfigDirectoryMonolithicDefault()
-> std::optional<std::string> 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;

View File

@ -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',

View File

@ -94,7 +94,7 @@ auto PythonClassBaseTimer::tp_new(PyTypeObject* type, PyObject* args,
self->timer_id_ = SceneV1Context::Current().NewTimer(
TimeType::kBase, static_cast<millisecs_t>(length * 1000.0),
static_cast<bool>(repeat),
Object::New<Runnable, base::PythonContextCallRunnable>(call_obj));
Object::New<Runnable, base::PythonContextCallRunnable>(call_obj).Get());
self->have_timer_ = true;
return reinterpret_cast<PyObject*>(self);

View File

@ -100,7 +100,7 @@ auto PythonClassSceneTimer::tp_new(PyTypeObject* type, PyObject* args,
self->timer_id_ = SceneV1Context::Current().NewTimer(
TimeType::kSim, static_cast<millisecs_t>(length * 1000.0),
static_cast<bool>(repeat),
Object::New<Runnable, base::PythonContextCallRunnable>(call_obj));
Object::New<Runnable, base::PythonContextCallRunnable>(call_obj).Get());
self->have_timer_ = true;
return reinterpret_cast<PyObject*>(self);

View File

@ -98,7 +98,7 @@ static auto PyTimer(PyObject* self, PyObject* args, PyObject* keywds)
SceneV1Context::Current().NewTimer(
TimeType::kSim, static_cast<millisecs_t>(length * 1000.0),
static_cast<bool>(repeat),
Object::New<Runnable, base::PythonContextCallRunnable>(call_obj));
Object::New<Runnable, base::PythonContextCallRunnable>(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<millisecs_t>(length * 1000.0),
static_cast<bool>(repeat),
Object::New<Runnable, base::PythonContextCallRunnable>(call_obj));
Object::New<Runnable, base::PythonContextCallRunnable>(call_obj).Get());
Py_RETURN_NONE;
BA_PYTHON_CATCH;

View File

@ -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>& 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>& 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>& 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) {

View File

@ -25,7 +25,7 @@ class HostActivity : public SceneV1Context {
// ContextTarget time/timer support.
auto NewTimer(TimeType timetype, TimerMedium length, bool repeat,
const Object::Ref<Runnable>& 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>& 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>& runnable) -> int;
auto NewBaseTimer(millisecs_t length, bool repeat, Runnable* runnable) -> int;
void DeleteBaseTimer(int timer_id);
void UpdateStepTimerLength();
void StepScene();

View File

@ -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>& runnable) -> int {
assert(runnable.IsValidManagedObject());
Runnable* runnable) -> int {
assert(Object::IsValidManagedObject(runnable));
// We currently support game and base timers.
switch (timetype) {

View File

@ -38,7 +38,7 @@ class HostSession : public Session {
// ContextTarget time/timer support
auto NewTimer(TimeType timetype, TimerMedium length, bool repeat,
const Object::Ref<Runnable>& runnable) -> int override;
Runnable* runnable) -> int override;
void DeleteTimer(TimeType timetype, int timer_id) override;
auto GetTime(TimeType timetype) -> millisecs_t override;

View File

@ -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>& 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:

View File

@ -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>& runnable) -> int;
Runnable* runnable) -> int;
virtual void DeleteTimer(TimeType timetype, int timer_id);
virtual auto GetTexture(const std::string& name) -> Object::Ref<SceneTexture>;

View File

@ -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;

View File

@ -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>& 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);
}

View File

@ -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>& runnable) -> Timer*;
auto NewTimer(millisecs_t length, bool repeat, Runnable* runnable) -> Timer*;
Timer* GetTimer(int id);
void DeleteTimer(int id);

View File

@ -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>& 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,

View File

@ -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>& 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*;

View File

@ -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)

View File

@ -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<int>(m.type)) {
touch_delay_timer_ = base::NewAppTimer(
touch_delay_timer_ = base::AppTimer::New(
150, false, [this] { OnTouchDelayTimerExpired(); });
}

View File

@ -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(); });
}
}

View File

@ -659,7 +659,6 @@ auto TextWidget::HandleMessage(const base::WidgetMessage& m) -> bool {
if (m.has_keysym && !ShouldUseStringEditor_()) {
last_carat_change_time_millisecs_ =
static_cast<millisecs_t>(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<uint32_t> unichars =
// Utils::UnicodeFromUTF8(text_raw_, "2jf987");
// int len = static_cast<int>(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;