mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-01-19 13:25:31 +08:00
more apple version work and cleaning up keyboard input
This commit is contained in:
parent
a2ff9ab872
commit
2ec08d706e
88
.efrocachemap
generated
88
.efrocachemap
generated
@ -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",
|
||||
|
||||
12
CHANGELOG.md
12
CHANGELOG.md
@ -1,4 +1,4 @@
|
||||
### 1.7.28 (build 21491, api 8, 2023-10-22)
|
||||
### 1.7.28 (build 21510, api 8, 2023-10-26)
|
||||
|
||||
- Massively cleaned up code related to rendering and window systems (OpenGL,
|
||||
SDL, etc). This code had been growing into a nasty tangle for 15 years
|
||||
@ -154,6 +154,16 @@
|
||||
seems pretty awesome these days. It should support most stuff SDL does and
|
||||
with less configuring involved. Please holler if you come across something
|
||||
that doesn't work.
|
||||
- Mac build is also now using the Game Controller Framework to handle keyboard
|
||||
events. This should better handle things like modifier keys and also will
|
||||
allow us to use that exact same code on the iPad/iPhone version.
|
||||
- OS key repeat events are no longer passed through the engine. This means that
|
||||
any time we want repeating behavior, such as holding an arrow key to move
|
||||
through UI elements, we will need to wire it up ourselves. We already do this
|
||||
for things like game controllers however, so this is more consistent in a way.
|
||||
- Dev console no longer claims key events unless the Python tab is showing and
|
||||
there is a hardware keyboard attached. This allows showing dev console tabs
|
||||
above gameplay without interfering with it.
|
||||
|
||||
### 1.7.27 (build 21282, api 8, 2023-08-30)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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" />
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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" />
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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'
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -99,6 +99,7 @@ class RenderPass;
|
||||
class RenderTarget;
|
||||
class RemoteAppServer;
|
||||
class RemoteControlInput;
|
||||
class Repeater;
|
||||
class ScoreToBeat;
|
||||
class AppAdapterSDL;
|
||||
class SDLContext;
|
||||
|
||||
@ -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());
|
||||
|
||||
|
||||
@ -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_;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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_{};
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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+E000–U+F8FF) we point at our own
|
||||
// custom page(s)
|
||||
bool covered{};
|
||||
|
||||
// For values in the custom-char range (U+E000–U+F8FF) we point at our
|
||||
// own custom page(s)
|
||||
if (val >= 0xE000 && val <= 0xF8FF) {
|
||||
// The 25 chars after this are in our fontExtras sheet.
|
||||
if (val < 0xE000 + 25) {
|
||||
// Special value denoting our custom font page.
|
||||
page = static_cast<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);
|
||||
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -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_;
|
||||
};
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
@ -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"; }
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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_
|
||||
|
||||
58
src/ballistica/base/support/repeater.cc
Normal file
58
src/ballistica/base/support/repeater.cc
Normal 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
|
||||
35
src/ballistica/base/support/repeater.h
Normal file
35
src/ballistica/base/support/repeater.h
Normal 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_
|
||||
@ -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");
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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>;
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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*;
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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(); });
|
||||
}
|
||||
|
||||
|
||||
@ -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(); });
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user