From f25f31cce4884a82ecbbda1b9d2fa2a20ddc7bb1 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 2 Oct 2023 16:14:47 -0700 Subject: [PATCH] work on user-defined dev-console tabs --- .efrocachemap | 60 ++-- .idea/dictionaries/ericf.xml | 3 + CHANGELOG.md | 2 +- Makefile | 12 +- .../.idea/dictionaries/ericf.xml | 3 + ballisticakit-cmake/CMakeLists.txt | 13 +- .../Generic/BallisticaKitGeneric.vcxproj | 8 +- .../Headless/BallisticaKitHeadless.vcxproj | 8 +- src/assets/.asset_manifest_public.json | 2 + src/assets/Makefile | 2 + src/assets/ba_data/python/babase/__init__.py | 8 + src/assets/ba_data/python/babase/_app.py | 2 + .../ba_data/python/babase/_devconsole.py | 144 +++++++++ src/assets/ba_data/python/babase/_hooks.py | 5 + src/assets/ba_data/python/baenv.py | 2 +- src/ballistica/base/audio/al_sys.h | 6 +- .../base/graphics/gl/renderer_gl.cc | 298 ++++++------------ src/ballistica/base/graphics/gl/renderer_gl.h | 44 +-- src/ballistica/base/python/base_python.h | 2 + .../python/methods/python_methods_misc.cc | 170 ++++++++++ src/ballistica/base/ui/dev_console.cc | 192 +++++++---- src/ballistica/base/ui/dev_console.h | 54 ++-- src/ballistica/shared/ballistica.cc | 2 +- .../shared/buildconfig/buildconfig_cmake.h | 2 +- src/ballistica/shared/python/python.cc | 2 +- src/ballistica/shared/python/python.h | 2 +- src/ballistica/shared/python/python_ref.cc | 8 +- src/ballistica/shared/python/python_ref.h | 1 + src/meta/babasemeta/pyembed/binding_base.py | 1 + .../babasemeta/pyembed/binding_base_app.py | 1 + tools/efro/cloudshell.py | 1 - tools/efrotools/pcommands.py | 10 +- 32 files changed, 703 insertions(+), 367 deletions(-) create mode 100644 src/assets/ba_data/python/babase/_devconsole.py diff --git a/.efrocachemap b/.efrocachemap index 194e6627..97fa6d11 100644 --- a/.efrocachemap +++ b/.efrocachemap @@ -4056,26 +4056,26 @@ "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": "fdcef1a7c77ad3cb23d7339fb1be2bf0", - "build/prefab/full/linux_arm64_gui/release/ballisticakit": "da17b5cc5f83bed3a0df7d238ba52d62", - "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "f550cb252d584f5ad7a100d9bb65d07a", - "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "6ce0af256949280a4820b96388e9d33c", - "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "65d75cd5909aa24e5c554c39d8aa7425", - "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "7e2267b47b1075da9737005d3d6821a3", - "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "6429f3fbddc8f71a29588486c9e0135f", - "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "48e5146f5a93e62d0c42d902d928f123", - "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "497c7b622b63b47268a4e55f439b00b1", - "build/prefab/full/mac_arm64_gui/release/ballisticakit": "2d54d79c942f48f6de4f07496b5c7d9c", - "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "0959c5af017a58f487d353d56cfb3fc5", - "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "ce4e095b7f36cf0f1c5d39bd196087a6", - "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "3afafa7c2b660d60c740bc152898f1b8", - "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "db0e1929b4fcfc55295fd401a8c19e3a", - "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "78c06393fc28e1d5ba8dac8ab97ba07e", - "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "fbada26a44865ad20b621641d4f1bf47", - "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "63d57d508d8f9ae21e05ceda181e1a2d", - "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "2c4f7247938de3d3fbb65b5766263866", - "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "85ef605dc94c4be74aac5b15a8931da5", - "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "a39d6180a89b83463fc44f1b006ebc7d", + "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "06130b0dfceeb424ba1b66414cf6d930", + "build/prefab/full/linux_arm64_gui/release/ballisticakit": "9ea9b12078443053eee0e0be5137492d", + "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "31f82f74a30d97d6bc66b2fa8d3abbcd", + "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "7ba3fa11b4e22b42aa16b8f0f904ea70", + "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "230141db6263d696c28ad00c011f0f9e", + "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "1e0a508a1fbd20f7e8a1ed79e361ecc6", + "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "ad6f15fdb1b3956b08d73abb4a0b240d", + "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "49edc04b213fd8bb10d3227e27921fe8", + "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "169375157927ea3a826d06a2f3339ccc", + "build/prefab/full/mac_arm64_gui/release/ballisticakit": "ed4a2bf5ef5c9e90c4a34a94e82e1752", + "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "4a8df3bd047824c139d271cb82db06d5", + "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "a4b0933ce9220822302d660865766891", + "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "a22db827c37da85d73d248be95f07523", + "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "49bb13ad22a1f5a75246a21830689998", + "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "98aa51efb0554fadd446f48b8cb6f31e", + "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "4450820bb5a73e7b83004a3fe78a50d6", + "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "cff958f088a5bea303a846dbd0d96e14", + "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "b2cbf2eee4db47d8d4e1ce0df1c80214", + "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "3aa845292833b423a7b96263b368bb13", + "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "2b2a64c6fb1d73d6ddfc19dc597a0947", "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "6ce4983e76e1cc2d2803fe306d08ad58", "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "4ea0cf78901f994215f215aebb0af1dc", "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "6ce4983e76e1cc2d2803fe306d08ad58", @@ -4092,18 +4092,18 @@ "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "de83e1b384abb333f457f2c9646f810f", "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "0149ef8dae7720330416846919a834e7", "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "de83e1b384abb333f457f2c9646f810f", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "e3ad15ecc9656a1a268582b14336bed7", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "fce3fd45ed78f84592ef914450817778", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "feeb14560de0fa18d8b7654aa43534ea", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "7896041f53b6f076d032388d661413b0", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "a5b84aaf028c8b13d55d2d99f3853f5d", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "4a8387851909f5cedabb5972b397a7b1", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "53fa53d605a009a6ab592e30d45629a8", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "2988d1791814f0ec0d64914d691a100e", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "163fbb4576a5218685919cf04de63cc6", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "66bbf458c1778685d38d6a2a537f6012", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "1ff129f72dc2dddc24d4dcdfb9b2826a", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "12d9566016a1bbd2aeb873d598d9ceed", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "ba99295cfbcbb3f6da27e4fad8227d74", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "8d53587d097910667fcb7f4958e55384", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "4f3e5d9578c0b0c3bd1203755fe8d464", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "ab834d0257bec7c972a2587a0b34eeff", "src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c", "src/assets/ba_data/python/babase/_mgen/enums.py": "f8cd3af311ac63147882590123b78318", - "src/ballistica/base/mgen/pyembed/binding_base.inc": "c81b2b1f3a14b4cd20a7b93416fe893a", - "src/ballistica/base/mgen/pyembed/binding_base_app.inc": "b67add3e1346f63491bf3450148e60d4", + "src/ballistica/base/mgen/pyembed/binding_base.inc": "9f71f171464dc004dbaab87e9bb4b03b", + "src/ballistica/base/mgen/pyembed/binding_base_app.inc": "a521bc86a7e98e56fec14cea029996f8", "src/ballistica/classic/mgen/pyembed/binding_classic.inc": "3ceb412513963f0818ab39c58bf292e3", "src/ballistica/core/mgen/pyembed/binding_core.inc": "9d0a3c9636138e35284923e0c8311c69", "src/ballistica/core/mgen/pyembed/env.inc": "8be46e5818f360d10b7b0224a9e91d07", diff --git a/.idea/dictionaries/ericf.xml b/.idea/dictionaries/ericf.xml index e14bdba4..c01d74a8 100644 --- a/.idea/dictionaries/ericf.xml +++ b/.idea/dictionaries/ericf.xml @@ -2923,6 +2923,8 @@ syslogmodule sysresponse tabdefs + tabentry + tabname tabtype tabtypes tabval @@ -3351,6 +3353,7 @@ xcworkspacedata xdrlib xhdpi + xhost xinput xjtp xmlbuilder diff --git a/CHANGELOG.md b/CHANGELOG.md index 9eaf9b4c..2dc786d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -### 1.7.28 (build 21401, api 8, 2023-09-29) +### 1.7.28 (build 21405, api 8, 2023-10-02) - 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 diff --git a/Makefile b/Makefile index a3c6c68e..f92aac5d 100644 --- a/Makefile +++ b/Makefile @@ -135,6 +135,7 @@ resources-clean: cd src/resources && $(MAKE) clean # Build our generated sources. +# # Meta builds can affect sources used by asset builds, resource builds, and # compiles, so it should be listed as a dependency of any of those. meta: prereqs @@ -146,8 +147,8 @@ meta-clean: rm -f $(LAZYBUILDDIR)/meta cd src/meta && $(MAKE) clean -# Remove ALL files and directories that aren't managed by git -# (except for a few things such as localconfig.json). +# Remove ALL files and directories that aren't managed by git (except for a +# few things such as localconfig.json). clean: $(CHECK_CLEAN_SAFETY) rm -rf build # Handle this part ourself; can confuse git. @@ -160,9 +161,10 @@ clean-list: git clean -dnx $(ROOT_CLEAN_IGNORES) # Build/update dummy python modules. -# IMPORTANT - building this target can kick off full builds/cleans and so -# it should not be built in parallel with other targets. -# See py_check_prereqs target for more info. +# +# IMPORTANT - building this target can kick off full builds/cleans and so it +# should not be built in parallel with other targets. See py_check_prereqs +# target for more info. dummymodules: prereqs meta @$(PCOMMAND) lazybuild dummymodules_src $(LAZYBUILDDIR)/$@ \ rm -rf build/dummymodules \&\& $(PCOMMAND) gen_dummy_modules diff --git a/ballisticakit-cmake/.idea/dictionaries/ericf.xml b/ballisticakit-cmake/.idea/dictionaries/ericf.xml index a7cc8acd..306ecee5 100644 --- a/ballisticakit-cmake/.idea/dictionaries/ericf.xml +++ b/ballisticakit-cmake/.idea/dictionaries/ericf.xml @@ -1733,6 +1733,8 @@ syscalls sysresponse tabdefs + tabentry + tabname tabtype tabtypes talloc @@ -1972,6 +1974,7 @@ xcrun xdiff xdist + xhost xinput xmax xmin diff --git a/ballisticakit-cmake/CMakeLists.txt b/ballisticakit-cmake/CMakeLists.txt index de4a7282..ba1dec31 100644 --- a/ballisticakit-cmake/CMakeLists.txt +++ b/ballisticakit-cmake/CMakeLists.txt @@ -51,11 +51,6 @@ if (HEADLESS) endif () find_package(OpenGL REQUIRED) find_package(OpenAL REQUIRED) - if (APPLE) - # On mac this sets an include path that we don't need since - # we're using the system framework... should clean this up. - set(OPENAL_INCLUDE_DIR "") - endif () find_library(OGG_LIBRARY ogg) find_library(VORBISFILE_LIBRARY vorbisfile) if (NOT OGG_LIBRARY) @@ -77,8 +72,7 @@ endif () # on Raspberry Pi builds. We never need to care about C++ abi compatibility # so just silencing them for now. Can maybe remove this later if they stop. if (CMAKE_CXX_COMPILER_ID MATCHES GNU) - set(CMAKE_CXX_FLAGS - "${CMAKE_CXX_FLAGS} -Wno-psabi") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-psabi") endif() set(BA_SRC_ROOT ../src) @@ -800,11 +794,6 @@ target_link_libraries(ballisticakitbin PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/prefablib/libballisticaplus.a ode pthread ${Python_LIBRARIES} ${SDL2_LIBRARIES} ${EXTRA_LIBRARIES} dl) -# Hack for building on rpi; need to update my pi so I can remove this. -if(EXISTS "/home/pi") -target_link_libraries(ballisticakitbin PRIVATE dl util stdc++fs) -endif() - # BallisticaKit modular shared library # (for use with vanilla Python interpreters). diff --git a/ballisticakit-windows/Generic/BallisticaKitGeneric.vcxproj b/ballisticakit-windows/Generic/BallisticaKitGeneric.vcxproj index 4f7d8d54..2c3bf6b5 100644 --- a/ballisticakit-windows/Generic/BallisticaKitGeneric.vcxproj +++ b/ballisticakit-windows/Generic/BallisticaKitGeneric.vcxproj @@ -98,7 +98,7 @@ false SyncCThrow true - ../../src;../../src/external/windows/include/SDL2;../../src/external/windows/include/python;../../src/external/windows/include;../../src/external/open_dynamics_engine-ef;../../src/external/qrencode-3.4.4 + ../../src;../../src/external/windows/include/SDL2;../../src/external/windows/include/AL;../../src/external/windows/include/python;../../src/external/windows/include;../../src/external/open_dynamics_engine-ef;../../src/external/qrencode-3.4.4 stdcpp17 Fast @@ -122,7 +122,7 @@ false SyncCThrow true - ../../src;../../src/external/windows/include/SDL2;../../src/external/windows/include/python;../../src/external/windows/include;../../src/external/open_dynamics_engine-ef + ../../src;../../src/external/windows/include/SDL2;../../src/external/windows/include/AL;../../src/external/windows/include/python;../../src/external/windows/include;../../src/external/open_dynamics_engine-ef stdcpp17 Fast @@ -147,7 +147,7 @@ true SyncCThrow false - ../../src;../../src/external/windows/include/SDL2;../../src/external/windows/include/python;../../src/external/windows/include;../../src/external/open_dynamics_engine-ef + ../../src;../../src/external/windows/include/SDL2;../../src/external/windows/include/AL;../../src/external/windows/include/python;../../src/external/windows/include;../../src/external/open_dynamics_engine-ef stdcpp17 Fast @@ -172,7 +172,7 @@ true SyncCThrow false - ../../src;../../src/external/windows/include/SDL2;../../src/external/windows/include/python;../../src/external/windows/include;../../src/external/open_dynamics_engine-ef + ../../src;../../src/external/windows/include/SDL2;../../src/external/windows/include/AL;../../src/external/windows/include/python;../../src/external/windows/include;../../src/external/open_dynamics_engine-ef stdcpp17 Fast diff --git a/ballisticakit-windows/Headless/BallisticaKitHeadless.vcxproj b/ballisticakit-windows/Headless/BallisticaKitHeadless.vcxproj index a3933aac..f6064e94 100644 --- a/ballisticakit-windows/Headless/BallisticaKitHeadless.vcxproj +++ b/ballisticakit-windows/Headless/BallisticaKitHeadless.vcxproj @@ -100,7 +100,7 @@ stdafx.h false SyncCThrow - ../../src;../../src/external/windows/include/SDL2;../../src/external/windows/include/python;../../src/external/windows/include;../../src/external/open_dynamics_engine-ef + ../../src;../../src/external/windows/include/SDL2;../../src/external/windows/include/AL;../../src/external/windows/include/python;../../src/external/windows/include;../../src/external/open_dynamics_engine-ef stdcpp17 Fast @@ -122,7 +122,7 @@ stdafx.h true false - ../../src;../../src/external/windows/include/SDL2;../../src/external/windows/include/python;../../src/external/windows/include;../../src/external/open_dynamics_engine-ef + ../../src;../../src/external/windows/include/SDL2;../../src/external/windows/include/AL;../../src/external/windows/include/python;../../src/external/windows/include;../../src/external/open_dynamics_engine-ef stdcpp17 Fast @@ -144,7 +144,7 @@ true stdafx.h SyncCThrow - ../../src;../../src/external/windows/include/SDL2;../../src/external/windows/include/python;../../src/external/windows/include;../../src/external/open_dynamics_engine-ef + ../../src;../../src/external/windows/include/SDL2;../../src/external/windows/include/AL;../../src/external/windows/include/python;../../src/external/windows/include;../../src/external/open_dynamics_engine-ef stdcpp17 Fast @@ -168,7 +168,7 @@ SyncCThrow stdafx.h true - ../../src;../../src/external/windows/include/SDL2;../../src/external/windows/include/python;../../src/external/windows/include;../../src/external/open_dynamics_engine-ef + ../../src;../../src/external/windows/include/SDL2;../../src/external/windows/include/AL;../../src/external/windows/include/python;../../src/external/windows/include;../../src/external/open_dynamics_engine-ef stdcpp17 Fast diff --git a/src/assets/.asset_manifest_public.json b/src/assets/.asset_manifest_public.json index 75eff2e4..9c4f56a5 100644 --- a/src/assets/.asset_manifest_public.json +++ b/src/assets/.asset_manifest_public.json @@ -14,6 +14,7 @@ "ba_data/python/babase/__pycache__/_assetmanager.cpython-311.opt-1.pyc", "ba_data/python/babase/__pycache__/_asyncio.cpython-311.opt-1.pyc", "ba_data/python/babase/__pycache__/_cloud.cpython-311.opt-1.pyc", + "ba_data/python/babase/__pycache__/_devconsole.cpython-311.opt-1.pyc", "ba_data/python/babase/__pycache__/_emptyappmode.cpython-311.opt-1.pyc", "ba_data/python/babase/__pycache__/_env.cpython-311.opt-1.pyc", "ba_data/python/babase/__pycache__/_error.cpython-311.opt-1.pyc", @@ -43,6 +44,7 @@ "ba_data/python/babase/_assetmanager.py", "ba_data/python/babase/_asyncio.py", "ba_data/python/babase/_cloud.py", + "ba_data/python/babase/_devconsole.py", "ba_data/python/babase/_emptyappmode.py", "ba_data/python/babase/_env.py", "ba_data/python/babase/_error.py", diff --git a/src/assets/Makefile b/src/assets/Makefile index 946ce5c0..5af7a60c 100644 --- a/src/assets/Makefile +++ b/src/assets/Makefile @@ -172,6 +172,7 @@ SCRIPT_TARGETS_PY_PUBLIC = \ $(BUILD_DIR)/ba_data/python/babase/_assetmanager.py \ $(BUILD_DIR)/ba_data/python/babase/_asyncio.py \ $(BUILD_DIR)/ba_data/python/babase/_cloud.py \ + $(BUILD_DIR)/ba_data/python/babase/_devconsole.py \ $(BUILD_DIR)/ba_data/python/babase/_emptyappmode.py \ $(BUILD_DIR)/ba_data/python/babase/_env.py \ $(BUILD_DIR)/ba_data/python/babase/_error.py \ @@ -445,6 +446,7 @@ SCRIPT_TARGETS_PYC_PUBLIC = \ $(BUILD_DIR)/ba_data/python/babase/__pycache__/_assetmanager.cpython-311.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/babase/__pycache__/_asyncio.cpython-311.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/babase/__pycache__/_cloud.cpython-311.opt-1.pyc \ + $(BUILD_DIR)/ba_data/python/babase/__pycache__/_devconsole.cpython-311.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/babase/__pycache__/_emptyappmode.cpython-311.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/babase/__pycache__/_env.cpython-311.opt-1.pyc \ $(BUILD_DIR)/ba_data/python/babase/__pycache__/_error.cpython-311.opt-1.pyc \ diff --git a/src/assets/ba_data/python/babase/__init__.py b/src/assets/ba_data/python/babase/__init__.py index 2f5ec2ac..4d5b7fb9 100644 --- a/src/assets/ba_data/python/babase/__init__.py +++ b/src/assets/ba_data/python/babase/__init__.py @@ -114,6 +114,11 @@ from babase._apputils import ( AppHealthMonitor, ) from babase._cloud import CloudSubsystem +from babase._devconsole import ( + DevConsoleTab, + DevConsoleTabEntry, + DevConsoleSubsystem, +) from babase._emptyappmode import EmptyAppMode from babase._error import ( print_exception, @@ -206,6 +211,9 @@ __all__ = [ 'ContextError', 'ContextRef', 'DelegateNotFoundError', + 'DevConsoleTab', + 'DevConsoleTabEntry', + 'DevConsoleSubsystem', 'DisplayTime', 'displaytime', 'displaytimer', diff --git a/src/assets/ba_data/python/babase/_app.py b/src/assets/ba_data/python/babase/_app.py index befcb788..91e11342 100644 --- a/src/assets/ba_data/python/babase/_app.py +++ b/src/assets/ba_data/python/babase/_app.py @@ -24,6 +24,7 @@ from babase._appcomponent import AppComponentSubsystem from babase._appmodeselector import AppModeSelector from babase._appintent import AppIntentDefault, AppIntentExec from babase._stringedit import StringEditSubsystem +from babase._devconsole import DevConsoleSubsystem if TYPE_CHECKING: import asyncio @@ -164,6 +165,7 @@ class App: self.workspaces = WorkspaceSubsystem() self.components = AppComponentSubsystem() self.stringedit = StringEditSubsystem() + self.devconsole = DevConsoleSubsystem() # This is incremented any time the app is backgrounded or # foregrounded; can be a simple way to determine if network data diff --git a/src/assets/ba_data/python/babase/_devconsole.py b/src/assets/ba_data/python/babase/_devconsole.py new file mode 100644 index 00000000..5cbc455f --- /dev/null +++ b/src/assets/ba_data/python/babase/_devconsole.py @@ -0,0 +1,144 @@ +# Released under the MIT License. See LICENSE for details. +# +"""Dev-Console functionality.""" +from __future__ import annotations + +from typing import TYPE_CHECKING +from dataclasses import dataclass +import logging + +import _babase + +if TYPE_CHECKING: + from typing import Callable, Any, Literal + + +class DevConsoleTab: + """Defines behavior for a tab in the dev-console.""" + + def refresh(self) -> None: + """Called when the tab should refresh itself.""" + + def request_refresh(self) -> None: + """The tab can call this to request that it be refreshed.""" + _babase.dev_console_request_refresh() + + def button( + self, + label: str, + pos: tuple[float, float], + size: tuple[float, float], + call: Callable[[], Any] | None = None, + h_anchor: Literal['left', 'center', 'right'] = 'center', + label_scale: float = 1.0, + corner_radius: float = 8.0, + ) -> None: + """Add a button to the tab being refreshed.""" + assert _babase.app.devconsole.is_refreshing + _babase.dev_console_add_button( + label, + pos[0], + pos[1], + size[0], + size[1], + call, + h_anchor, + label_scale, + corner_radius, + ) + + def python_terminal(self) -> None: + """Add a Python Terminal to the tab being refreshed.""" + assert _babase.app.devconsole.is_refreshing + _babase.dev_console_add_python_terminal() + + @property + def width(self) -> float: + """Return the current tab width. Only call during refreshes.""" + assert _babase.app.devconsole.is_refreshing + return _babase.dev_console_tab_width() + + @property + def height(self) -> float: + """Return the current tab height. Only call during refreshes.""" + assert _babase.app.devconsole.is_refreshing + return _babase.dev_console_tab_height() + + @property + def base_scale(self) -> float: + """A scale value set depending on the app's UI scale. + + Dev-console tabs can incorporate this into their UI sizes and + positions if they desire. This must be done manually however. + """ + assert _babase.app.devconsole.is_refreshing + return _babase.dev_console_base_scale() + + +class DevConsoleTabPython(DevConsoleTab): + """The Python dev-console tab.""" + + def refresh(self) -> None: + self.python_terminal() + + +class DevConsoleTabTest(DevConsoleTab): + """Test dev-console tab.""" + + def refresh(self) -> None: + import random + + self.button( + f'FLOOP-{random.randrange(200)}', + pos=(10, 10), + size=(100, 30), + h_anchor='left', + call=self.request_refresh, + ) + + +@dataclass +class DevConsoleTabEntry: + """Represents a distinct tab in the dev-console.""" + + name: str + factory: Callable[[], DevConsoleTab] + + +class DevConsoleSubsystem: + """Wrangles the dev console.""" + + def __init__(self) -> None: + # All tabs in the dev-console. Add your own stuff here via + # plugins or whatnot. + self.tabs: list[DevConsoleTabEntry] = [ + DevConsoleTabEntry('Python', DevConsoleTabPython), + DevConsoleTabEntry('Test', DevConsoleTabTest), + ] + self.is_refreshing = False + + def do_refresh_tab(self, tabname: str) -> None: + """Called by the C++ layer when a tab should be filled out.""" + assert _babase.in_logic_thread() + + # FIXME: We currently won't handle multiple tabs with the same + # name. We should give a clean error or something in that case. + tab: DevConsoleTab | None = None + for tabentry in self.tabs: + if tabentry.name == tabname: + tab = tabentry.factory() + break + + if tab is None: + logging.error( + 'DevConsole got refresh request for tab' + " '%s' which does not exist.", + tabname, + ) + return + + self.is_refreshing = True + try: + tab.refresh() + finally: + self.is_refreshing = False diff --git a/src/assets/ba_data/python/babase/_hooks.py b/src/assets/ba_data/python/babase/_hooks.py index 5013b956..90dde9be 100644 --- a/src/assets/ba_data/python/babase/_hooks.py +++ b/src/assets/ba_data/python/babase/_hooks.py @@ -372,3 +372,8 @@ def string_edit_adapter_can_be_replaced(adapter: StringEditAdapter) -> bool: assert isinstance(adapter, StringEditAdapter) return adapter.can_be_replaced() + + +def get_dev_console_tab_names() -> list[str]: + """Return the current set of dev-console tab names.""" + return [t.name for t in _babase.app.devconsole.tabs] diff --git a/src/assets/ba_data/python/baenv.py b/src/assets/ba_data/python/baenv.py index 2e3ed75e..5c0b051b 100644 --- a/src/assets/ba_data/python/baenv.py +++ b/src/assets/ba_data/python/baenv.py @@ -52,7 +52,7 @@ if TYPE_CHECKING: # Build number and version of the ballistica binary we expect to be # using. -TARGET_BALLISTICA_BUILD = 21401 +TARGET_BALLISTICA_BUILD = 21405 TARGET_BALLISTICA_VERSION = '1.7.28' diff --git a/src/ballistica/base/audio/al_sys.h b/src/ballistica/base/audio/al_sys.h index ad69ffd3..475af2db 100644 --- a/src/ballistica/base/audio/al_sys.h +++ b/src/ballistica/base/audio/al_sys.h @@ -13,12 +13,12 @@ #include #include #else -#include -#include +#include +#include #endif #if BA_OSTYPE_ANDROID -#include +#include #endif #define CHECK_AL_ERROR _check_al_error(__FILE__, __LINE__) diff --git a/src/ballistica/base/graphics/gl/renderer_gl.cc b/src/ballistica/base/graphics/gl/renderer_gl.cc index 9238619d..ad6dcf46 100644 --- a/src/ballistica/base/graphics/gl/renderer_gl.cc +++ b/src/ballistica/base/graphics/gl/renderer_gl.cc @@ -1,15 +1,12 @@ // Released under the MIT License. See LICENSE for details. #if BA_ENABLE_OPENGL - #include "ballistica/base/graphics/gl/renderer_gl.h" #include #include #include "ballistica/base/graphics/component/special_component.h" -#include "ballistica/base/graphics/gl/framebuffer_object_gl.h" -#include "ballistica/base/graphics/gl/gl_sys.h" #include "ballistica/base/graphics/gl/mesh/mesh_asset_data_gl.h" #include "ballistica/base/graphics/gl/mesh/mesh_data_dual_texture_full_gl.h" #include "ballistica/base/graphics/gl/mesh/mesh_data_gl.h" @@ -29,11 +26,6 @@ #include "ballistica/base/graphics/gl/render_target_gl.h" #include "ballistica/base/graphics/gl/texture_data_gl.h" #include "ballistica/base/platform/base_platform.h" -#include "ballistica/shared/buildconfig/buildconfig_common.h" - -#if BA_OSTYPE_IOS_TVOS -#include "ballistica/base/platform/apple/apple_utils.h" -#endif // Turn this off to see how much blend overdraw is occurring. #define BA_GL_ENABLE_BLEND 1 @@ -64,22 +56,6 @@ namespace ballistica::base { bool RendererGL::funky_depth_issue_set_{}; bool RendererGL::funky_depth_issue_{}; -bool RendererGL::draws_shields_funny_{}; -bool RendererGL::draws_shields_funny_set_{}; - -// FIXME - move this stuff to the renderer class. -// GLint g_combined_texture_image_unit_count{}; -// bool g_anisotropic_support{}; -// bool g_vao_support{}; -// float g_max_anisotropy{}; -// bool g_discard_framebuffer_support{}; -// bool g_invalidate_framebuffer_support{}; -// bool g_blit_framebuffer_support{}; -// bool g_framebuffer_multisample_support{}; -// bool g_running_es3{}; -// bool g_seamless_cube_maps{}; -// int g_msaa_max_samples_rgb565{}; -// int g_msaa_max_samples_rgb8{}; #if BA_OSTYPE_ANDROID bool RendererGL::is_speedy_android_device_{}; @@ -108,8 +84,11 @@ void RendererGL::CheckGLError(const char* file, int line) { GLenum err = glGetError(); if (err != GL_NO_ERROR) { const char* version = (const char*)glGetString(GL_VERSION); + BA_PRECONDITION_FATAL(version); const char* vendor = (const char*)glGetString(GL_VENDOR); + BA_PRECONDITION_FATAL(vendor); const char* renderer = (const char*)glGetString(GL_RENDERER); + BA_PRECONDITION_FATAL(renderer); Log(LogLevel::kError, "OpenGL Error at " + std::string(file) + " line " + std::to_string(line) + ": " + GLErrorToString(err) + "\nrenderer: " + renderer @@ -177,11 +156,27 @@ void RendererGL::CheckGLCapabilities_() { BA_DEBUG_CHECK_GL_ERROR; assert(g_base->app_adapter->InGraphicsContext()); - draws_shields_funny_set_ = true; - const char* renderer = (const char*)glGetString(GL_RENDERER); + BA_PRECONDITION_FATAL(renderer); const char* vendor = (const char*)glGetString(GL_VENDOR); + BA_PRECONDITION_FATAL(vendor); const char* version_str = (const char*)glGetString(GL_VERSION); + BA_PRECONDITION_FATAL(version_str); + + // Do a rough check to make sure we're running 3 or newer of GL/GLES. + // This query should be available even on older versions. + if (version_str[0] != '3' && version_str[0] != '4') { + FatalError(std::string("Invalid OpenGL version found (") + version_str + + "). We require 3.0 or later."); + } + + // Now fetch exact major/minor versions. This query requires version 3.0 + // or newer which is why we checked that above. + glGetError(); // Clear any existing error so we don't die on it here. + glGetIntegerv(GL_MAJOR_VERSION, &gl_version_major_); + BA_PRECONDITION_FATAL(glGetError() == GL_NO_ERROR); + glGetIntegerv(GL_MINOR_VERSION, &gl_version_minor_); + BA_PRECONDITION_FATAL(glGetError() == GL_NO_ERROR); const char* basestr; if (gl_is_es()) { @@ -200,28 +195,20 @@ void RendererGL::CheckGLCapabilities_() { std::vector extensions; bool used_num_extensions{}; - // On the ES side we still support ES 2 which does not support the modern - // GL_NUM_EXTENSIONS route. Though I suppose we could just try it on ES - // and do the fallback if we get GL_INVALID_ENUM. -#if !BA_OPENGL_IS_ES - { - GLint num_extensions{}; - glGetError(); // Clear any existing error. - glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions); - if (glGetError() == GL_NO_ERROR) { - used_num_extensions = true; - extensions.reserve(num_extensions); - for (int i = 0; i < num_extensions; ++i) { - const char* extension = (const char*)glGetStringi(GL_EXTENSIONS, i); - BA_PRECONDITION(extension); - extensions.push_back(extension); - } + // Do the modern gl thing of looking through a list of extensions; not a + // single string. + if (auto num_extensions = GLGetIntOptional(GL_NUM_EXTENSIONS)) { + used_num_extensions = true; + extensions.reserve(*num_extensions); + for (int i = 0; i < num_extensions; ++i) { + const char* extension = (const char*)glGetStringi(GL_EXTENSIONS, i); + BA_PRECONDITION(extension); + extensions.push_back(extension); } - } -#endif - - // Fall back on parsing the single giant string if need be. - if (!used_num_extensions) { + } else { + Log(LogLevel::kWarning, "Falling back on legacy GL_EXTENSIONS parsing."); + // Fall back on parsing the single giant string if need be. + // (Can probably kill this). auto* ex = reinterpret_cast(glGetString(GL_EXTENSIONS)); BA_DEBUG_CHECK_GL_ERROR; BA_PRECONDITION_FATAL(ex); @@ -349,13 +336,15 @@ void RendererGL::CheckGLCapabilities_() { std::list c_types; assert(g_base->graphics); - if (CheckGLExtension(extensions, "texture_compression_s3tc")) + if (CheckGLExtension(extensions, "texture_compression_s3tc")) { c_types.push_back(TextureCompressionType::kS3TC); + } // Limiting pvr support to iOS for the moment. if (!g_buildconfig.ostype_android()) { - if (CheckGLExtension(extensions, "texture_compression_pvrtc")) + if (CheckGLExtension(extensions, "texture_compression_pvrtc")) { c_types.push_back(TextureCompressionType::kPVR); + } } // All android devices should support etc1. @@ -375,33 +364,14 @@ void RendererGL::CheckGLCapabilities_() { g_base->graphics_server->SetTextureCompressionTypes(c_types); - // Check whether we support high-quality mode (requires a few things like - // depth textures) For now lets also disallow high-quality in some VR - // environments. - - // Both GL 3.2 and GL ES 3.0 support depth textures. - // if (!gl_is_es()) { - // supports_depth_textures_ = true; + // Both GL 3 and GL ES 3.0 support depth textures (and thus our high + // quality mode) as a core feature. g_base->graphics->SetSupportsHighQualityGraphics(true); - // } else { - // if (CheckGLExtension(extensions, "depth_texture")) { - // supports_depth_textures_ = true; - // if (g_buildconfig.cardboard_build()) { - // g_base->graphics->SetSupportsHighQualityGraphics(false); - // } else { - // g_base->graphics->SetSupportsHighQualityGraphics(true); - // } - // } else { - // supports_depth_textures_ = false; - // g_base->graphics->SetSupportsHighQualityGraphics(false); - // } - // } - // Store the tex-compression type we support. BA_DEBUG_CHECK_GL_ERROR; - // Anisotropic sampling is still an extension as of both GL 3.2 and ES 3, + // Anisotropic sampling is still an extension as of both GL 3 and ES 3, // so we need to test for it. anisotropic_support_ = CheckGLExtension(extensions, "texture_filter_anisotropic"); @@ -411,40 +381,6 @@ void RendererGL::CheckGLCapabilities_() { BA_DEBUG_CHECK_GL_ERROR; - // VAO support is everywhere now. - // #if BA_OPENGL_IS_ES - // // We can run with our without VAOs but they're nice to have. - // g_vao_support = - // (glGenVertexArrays != nullptr && glDeleteVertexArrays != nullptr - // && glBindVertexArray != nullptr - // && (g_running_es3 - // || CheckGLExtension(extensions, "vertex_array_object"))); - // #else - // g_vao_support = true; - // #endif - - // #if !BA_OPENGL_IS_ES - // Both of these are standard in GL 3.2 and GL ES 3; hooray! - // g_blit_framebuffer_support = true; - // g_framebuffer_multisample_support = true; - // #else - // #if BA_OSTYPE_IOS_TVOS - // g_blit_framebuffer_support = false; - // g_framebuffer_multisample_support = false; - // #elif BA_OSTYPE_MACOS - // g_blit_framebuffer_support = CheckGLExtension(extensions, - // "framebuffer_blit"); g_framebuffer_multisample_support = false; - // #else - // g_blit_framebuffer_support = - // (glBlitFramebuffer != nullptr - // && (g_running_es3 || CheckGLExtension(ex, "framebuffer_blit"))); - // g_framebuffer_multisample_support = - // (glRenderbufferStorageMultisample != nullptr - // && (g_running_es3 || (CheckGLExtension(ex, - // "framebuffer_multisample")))); - // #endif - // #endif - if (gl_is_es()) { // GL ES 3 has glInvalidateFramebuffer as part of the standard. invalidate_framebuffer_support_ = true; @@ -454,68 +390,25 @@ void RendererGL::CheckGLCapabilities_() { invalidate_framebuffer_support_ = false; } - // #if BA_OSTYPE_IOS_TVOS || BA_OSTYPE_ANDROID - // #if BA_OSTYPE_IOS_TVOS - // g_discard_framebuffer_support = CheckGLExtension(ex, - // "discard_framebuffer"); - // #else - // g_discard_framebuffer_support = - // (glDiscardFramebufferEXT != nullptr - // && CheckGLExtension(ex, "discard_framebuffer")); - // #endif - - // g_invalidate_framebuffer_support = - // (g_running_es3 && glInvalidateFramebuffer != nullptr); - // #else - // g_discard_framebuffer_support = false; - // g_invalidate_framebuffer_support = false; - // #endif - - // g_seamless_cube_maps = CheckGLExtension(ex, "seamless_cube_map"); - - // #if BA_OSTYPE_WINDOWS - // // The vmware gl driver breaks horrifically with VAOs turned on. - // const char* vendor = (const char*)glGetString(GL_VENDOR); - // if (strstr(vendor, "VMware")) { - // g_vao_support = false; - // } - // #endif - - // #if BA_OSTYPE_ANDROID - // // VAOs currently break my poor kindle fire hd to the point of rebooting - // it if (!g_running_es3 && !is_tegra_4_) { - // g_vao_support = false; - // } - - // // also they seem to be problematic on zenfone2's gpu. - // if (strstr(renderer, "PowerVR Rogue G6430")) { - // g_vao_support = false; - // } - // #endif - - glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, - &combined_texture_image_unit_count_); + combined_texture_image_unit_count_ = + GLGetInt(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS); // If we're running ES3, ask about our max multisample counts and whether we // can enable MSAA. - // enable_msaa_ = false; // start pessimistic msaa_max_samples_rgb565_ = msaa_max_samples_rgb8_ = 0; // start pessimistic - // FIXME - NEED TO CHECK DESKTOP GL VERSION TO USE THIS THERE. -#if BA_OSTYPE_ANDROID || BA_RIFT_BUILD - bool check_msaa = false; + bool have_gl_get_internal_format_iv{}; + if (gl_is_es()) { + // This is available on ES 3. + have_gl_get_internal_format_iv = true; + } else { + // This is available on GL 4.2 or newer. + if (gl_version_major() == 4 && gl_version_minor() >= 2) { + have_gl_get_internal_format_iv = true; + } + } -#if BA_OSTYPE_ANDROID - // if (g_running_es3) { - check_msaa = true; - // } -#endif // BA_OSTYPE_ANDROID - -#if BA_RIFT_BUILD - check_msaa = true; -#endif // BA_RIFT_BUILD - - if (check_msaa) { + if (have_gl_get_internal_format_iv) { GLint count; glGetInternalformativ(GL_RENDERBUFFER, GL_RGB565, GL_NUM_SAMPLE_COUNTS, 1, &count); @@ -543,10 +436,17 @@ void RendererGL::CheckGLCapabilities_() { BA_LOG_ONCE(LogLevel::kError, "Got 0 samplecounts for RGB8"); msaa_max_samples_rgb8_ = 0; } + } else { + // For older GL (which includes all Macs) it sounds like this is the way + // to query max samples?.. but I don't know for sure if this applies to + // renderbuffer targets or just the default drawable. Will it ever be + // different? + auto max_samples = GLGetIntOptional(GL_MAX_SAMPLES); + if (max_samples.has_value()) { + msaa_max_samples_rgb565_ = msaa_max_samples_rgb8_ = *max_samples; + } } -#endif // BA_OSTYPE_ANDROID - BA_DEBUG_CHECK_GL_ERROR; first_extension_check_ = false; @@ -639,7 +539,7 @@ void RendererGL::BindTextureUnit(uint32_t tex_unit) { assert(tex_unit >= 0 && tex_unit < kMaxGLTexUnitsUsed); if (active_tex_unit_ != -1) { // Make sure our internal state stays correct. - assert(DebugGLGetInt(GL_ACTIVE_TEXTURE) == GL_TEXTURE0 + active_tex_unit_); + assert(GLGetInt(GL_ACTIVE_TEXTURE) == GL_TEXTURE0 + active_tex_unit_); } if (active_tex_unit_ != tex_unit) { active_tex_unit_ = tex_unit; @@ -649,18 +549,39 @@ void RendererGL::BindTextureUnit(uint32_t tex_unit) { } } -auto RendererGL::DebugGLGetInt(GLenum name) -> int { - // This is probably inefficient so make sure we don't leave it on in - // release builds. - assert(g_buildconfig.debug_build()); +auto RendererGL::GLGetInt(GLenum name) -> int { assert(g_base->app_adapter->InGraphicsContext()); - // Clear any error coming in; don't want to die for something that's not - // ours. - BA_DEBUG_CHECK_GL_ERROR; + // Clear any error coming in; don't want to fail for something that's not + // our problem. + if (g_buildconfig.debug_build()) { + BA_DEBUG_CHECK_GL_ERROR; + } else { + glGetError(); + } GLint val; glGetIntegerv(name, &val); - assert(glGetError() == GL_NO_ERROR); + if (glGetError() != GL_NO_ERROR) { + FatalError("Unable to fetch GL int " + std::to_string(name)); + } + return val; +} + +auto RendererGL::GLGetIntOptional(GLenum name) -> std::optional { + assert(g_base->app_adapter->InGraphicsContext()); + + // Clear any error coming in; don't want to fail for something that's not + // our problem. + if (g_buildconfig.debug_build()) { + BA_DEBUG_CHECK_GL_ERROR; + } else { + glGetError(); + } + GLint val; + glGetIntegerv(name, &val); + if (glGetError() != GL_NO_ERROR) { + return {}; + } return val; } @@ -669,14 +590,14 @@ void RendererGL::BindFramebuffer(GLuint fb) { glBindFramebuffer(GL_FRAMEBUFFER, fb); active_framebuffer_ = fb; } else { - assert(DebugGLGetInt(GL_FRAMEBUFFER_BINDING) == fb); + assert(GLGetInt(GL_FRAMEBUFFER_BINDING) == fb); } } void RendererGL::BindArrayBuffer(GLuint b) { if (active_array_buffer_ != -1) { // Make sure our internal state stays correct. - assert(DebugGLGetInt(GL_ARRAY_BUFFER_BINDING) == active_array_buffer_); + assert(GLGetInt(GL_ARRAY_BUFFER_BINDING) == active_array_buffer_); } if (active_array_buffer_ != b) { glBindBuffer(GL_ARRAY_BUFFER, b); @@ -702,7 +623,7 @@ void RendererGL::BindTexture_(GLuint type, GLuint tex, GLuint tex_unit) { if (g_buildconfig.debug_build()) { if (bound_textures_2d_[tex_unit] != -1) { BindTextureUnit(tex_unit); - assert(DebugGLGetInt(GL_TEXTURE_BINDING_2D) + assert(GLGetInt(GL_TEXTURE_BINDING_2D) == bound_textures_2d_[tex_unit]); } } @@ -718,7 +639,7 @@ void RendererGL::BindTexture_(GLuint type, GLuint tex, GLuint tex_unit) { if (g_buildconfig.debug_build()) { if (bound_textures_cube_map_[tex_unit] != -1) { BindTextureUnit(tex_unit); - assert(DebugGLGetInt(GL_TEXTURE_BINDING_CUBE_MAP) + assert(GLGetInt(GL_TEXTURE_BINDING_CUBE_MAP) == bound_textures_cube_map_[tex_unit]); } } @@ -825,7 +746,6 @@ void RendererGL::InvalidateFramebuffer(bool color, bool depth, // Currently this is ES only for us. #if BA_OPENGL_IS_ES - // #if BA_OSTYPE_IOS_TVOS || BA_OSTYPE_ANDROID if (invalidate_framebuffer_support()) { GLenum attachments[5]; @@ -834,11 +754,9 @@ void RendererGL::InvalidateFramebuffer(bool color, bool depth, if (active_framebuffer_ == 0 && !target_read_framebuffer) { if (color) { attachments[count++] = GL_COLOR; - // attachments[count++] = GL_COLOR_EXT; } if (depth) { attachments[count++] = GL_DEPTH; - // attachments[count++] = GL_DEPTH_EXT; } } else { if (color) { @@ -848,29 +766,16 @@ void RendererGL::InvalidateFramebuffer(bool color, bool depth, attachments[count++] = GL_DEPTH_ATTACHMENT; } - // Apparently the oculus docs say glInvalidateFramebuffer errors on a - // mali es3 implementation so they always use glDiscard when present. - // if () { - // #if BA_OSTYPE_IOS_TVOS - // throw Exception(); // shouldnt happen - // #else glInvalidateFramebuffer( target_read_framebuffer ? GL_READ_FRAMEBUFFER : GL_FRAMEBUFFER, count, attachments); - // #endif } - // } else { - // // If we've got a read-framebuffer, we should have invalidate too. - // assert(!target_read_framebuffer); - // glDiscardFramebufferEXT(GL_FRAMEBUFFER, count, attachments); - // } BA_DEBUG_CHECK_GL_ERROR; } #else - // Make noise if we should be doing this here too... + // Make noise if we should be doing this here too at some point. assert(!invalidate_framebuffer_support()); #endif // BA_OPENGL_IS_ES - // #endif // BA_OSTYPE_IOS_TVOS || BA_OSTYPE_ANDROID } RendererGL::~RendererGL() { @@ -2701,8 +2606,7 @@ void RendererGL::Load() { // Grab the current framebuffer and consider that to be our 'screen' // framebuffer. This can be 0 for the main framebuffer or can be // something else. - glGetIntegerv(GL_FRAMEBUFFER_BINDING, - reinterpret_cast(&screen_framebuffer_)); + screen_framebuffer_ = GLGetInt(GL_FRAMEBUFFER_BINDING); } Renderer::Load(); int high_qual_pp_flag = @@ -3340,9 +3244,7 @@ void RendererGL::VREyeRenderBegin() { glDisable(GL_FRAMEBUFFER_SRGB); #endif // BA_RIFT_BUILD - GLuint fb; - glGetIntegerv(GL_FRAMEBUFFER_BINDING, reinterpret_cast(&fb)); - screen_framebuffer_ = fb; + screen_framebuffer_ = GLGetInt(GL_FRAMEBUFFER_BINDING); } #if BA_VR_BUILD diff --git a/src/ballistica/base/graphics/gl/renderer_gl.h b/src/ballistica/base/graphics/gl/renderer_gl.h index 66927c34..c3afef5a 100644 --- a/src/ballistica/base/graphics/gl/renderer_gl.h +++ b/src/ballistica/base/graphics/gl/renderer_gl.h @@ -217,7 +217,14 @@ class RendererGL : public Renderer { #endif } - auto DebugGLGetInt(GLenum name) -> int; + auto gl_version_minor() const { return gl_version_minor_; } + auto gl_version_major() const { return gl_version_major_; } + + // Wraps glGetIntegerv(). Triggers FatalError if get fails. + auto GLGetInt(GLenum name) -> int; + + // Wraps glGetIntegerv(); returns empty value if get fails. + auto GLGetIntOptional(GLenum name) -> std::optional; private: static auto GetFunkyDepthIssue_() -> bool; @@ -251,18 +258,28 @@ class RendererGL : public Renderer { void BindArrayBuffer(GLuint b); void SetBlend(bool b); void SetBlendPremult(bool b); - millisecs_t dof_update_time_{}; - std::vector > blur_buffers_; - // bool supports_depth_textures_{}; + + bool blend_{}; + bool blend_premult_{}; bool first_extension_check_{true}; bool is_tegra_4_{}; bool is_tegra_k1_{}; bool is_recent_adreno_{}; bool is_adreno_{}; bool enable_msaa_{}; + bool draw_at_equal_depth_{}; + bool depth_writing_enabled_{}; + bool depth_testing_enabled_{}; + bool data_loaded_{}; + bool draw_front_{}; + bool got_screen_framebuffer_{}; + bool double_sided_{}; + bool invalidate_framebuffer_support_{}; + GLint gl_version_major_{}; + GLint gl_version_minor_{}; + int last_blur_res_count_{}; float last_cam_buffer_width_{}; float last_cam_buffer_height_{}; - int last_blur_res_count_{}; float vignette_tex_outer_r_{}; float vignette_tex_outer_g_{}; float vignette_tex_outer_b_{}; @@ -271,13 +288,7 @@ class RendererGL : public Renderer { float vignette_tex_inner_b_{}; float depth_range_min_{}; float depth_range_max_{}; - bool draw_at_equal_depth_{}; - bool depth_writing_enabled_{}; - bool depth_testing_enabled_{}; - bool data_loaded_{}; - bool draw_front_{}; - bool got_screen_framebuffer_{}; - GLuint screen_framebuffer_{}; + GLint screen_framebuffer_{}; GLuint random_tex_{}; GLuint vignette_tex_{}; GraphicsQuality vignette_quality_{}; @@ -285,6 +296,8 @@ class RendererGL : public Renderer { GLint viewport_y_{}; GLint viewport_width_{}; GLint viewport_height_{}; + millisecs_t dof_update_time_{}; + std::vector > blur_buffers_; std::vector > shaders_; ProgramSimpleGL* simple_color_prog_{}; ProgramSimpleGL* simple_tex_prog_{}; @@ -326,24 +339,18 @@ class RendererGL : public Renderer { ProgramPostProcessGL* postprocess_distort_prog_{}; static bool funky_depth_issue_set_; static bool funky_depth_issue_; - static bool draws_shields_funny_set_; - static bool draws_shields_funny_; #if BA_OSTYPE_ANDROID static bool is_speedy_android_device_; static bool is_extra_speedy_android_device_; #endif ProgramGL* current_program_{}; - bool double_sided_{}; std::vector scissor_rects_; GLuint current_vertex_array_{}; - bool vertex_attrib_arrays_enabled_[kVertexAttrCount]{}; int active_tex_unit_{}; int active_framebuffer_{}; int active_array_buffer_{}; int bound_textures_2d_[kMaxGLTexUnitsUsed]{}; int bound_textures_cube_map_[kMaxGLTexUnitsUsed]{}; - bool blend_{}; - bool blend_premult_{}; std::unique_ptr screen_mesh_; std::vector recycle_mesh_datas_simple_split_; std::vector recycle_mesh_datas_object_split_; @@ -355,7 +362,6 @@ class RendererGL : public Renderer { GLint combined_texture_image_unit_count_{}; GLint anisotropic_support_{}; GLfloat max_anisotropy_{}; - bool invalidate_framebuffer_support_{}; int msaa_max_samples_rgb565_{-1}; int msaa_max_samples_rgb8_{-1}; }; diff --git a/src/ballistica/base/python/base_python.h b/src/ballistica/base/python/base_python.h index c4934031..5764fd79 100644 --- a/src/ballistica/base/python/base_python.h +++ b/src/ballistica/base/python/base_python.h @@ -105,6 +105,8 @@ class BasePython { kAppPushApplyAppConfigCall, kStringEditAdapterCanBeReplacedCall, kDevConsoleStringEditAdapterClass, + kGetDevConsoleTabNamesCall, + kAppDevConsoleDoRefreshTabCall, kLast // Sentinel; must be at end. }; diff --git a/src/ballistica/base/python/methods/python_methods_misc.cc b/src/ballistica/base/python/methods/python_methods_misc.cc index 427f1e82..9ff92423 100644 --- a/src/ballistica/base/python/methods/python_methods_misc.cc +++ b/src/ballistica/base/python/methods/python_methods_misc.cc @@ -12,6 +12,7 @@ #include "ballistica/base/python/base_python.h" #include "ballistica/base/python/class/python_class_simple_sound.h" #include "ballistica/base/support/app_config.h" +#include "ballistica/base/ui/dev_console.h" #include "ballistica/base/ui/ui.h" #include "ballistica/shared/generic/utils.h" @@ -1450,6 +1451,169 @@ static PyMethodDef PyFatalErrorDef = { "however, Exceptions should be preferred.", }; +// ------------------------- dev_console_add_button ---------------------------- + +static auto PyDevConsoleAddButton(PyObject* self, PyObject* args) -> PyObject* { + BA_PYTHON_TRY; + BA_PRECONDITION(g_base->InLogicThread()); + auto* dev_console = g_base->ui->dev_console(); + BA_PRECONDITION(dev_console); + BA_PRECONDITION(dev_console->IsActive()); + const char* label; + float x; + float y; + float width; + float height; + PyObject* call; + const char* h_anchor; + float label_scale; + float corner_radius; + if (!PyArg_ParseTuple(args, "sffffOsff", &label, &x, &y, &width, &height, + &call, &h_anchor, &label_scale, &corner_radius)) { + return nullptr; + } + dev_console->AddButton(label, x, y, width, height, call, h_anchor, + label_scale, corner_radius); + Py_RETURN_NONE; + BA_PYTHON_CATCH; +} + +static PyMethodDef PyDevConsoleAddButtonDef = { + "dev_console_add_button", // name + (PyCFunction)PyDevConsoleAddButton, // method + METH_VARARGS, // flags + + "dev_console_add_button(\n" + " label: str,\n" + " x: float,\n" + " y: float,\n" + " width: float,\n" + " height: float,\n" + " call: Callable[[], Any] | None,\n" + " h_anchor: str,\n" + " label_scale: float,\n" + " corner_radius: float,\n" + ") -> None\n" + "\n" + "(internal)", +}; + +// -------------------- dev_console_add_python_terminal ------------------------ + +static auto PyDevConsoleAddPythonTerminal(PyObject* self, PyObject* args) + -> PyObject* { + BA_PYTHON_TRY; + BA_PRECONDITION(g_base->InLogicThread()); + auto* dev_console = g_base->ui->dev_console(); + BA_PRECONDITION(dev_console); + BA_PRECONDITION(dev_console->IsActive()); + if (!PyArg_ParseTuple(args, "")) { + return nullptr; + } + dev_console->AddPythonTerminal(); + Py_RETURN_NONE; + BA_PYTHON_CATCH; +} + +static PyMethodDef PyDevConsoleAddPythonTerminalDef = { + "dev_console_add_python_terminal", // name + (PyCFunction)PyDevConsoleAddPythonTerminal, // method + METH_VARARGS, // flags + + "dev_console_add_python_terminal() -> None\n" + "\n" + "(internal)", +}; + +// ------------------------ dev_console_tab_width ------------------------------ + +static auto PyDevConsoleTabWidth(PyObject* self) -> PyObject* { + BA_PYTHON_TRY; + BA_PRECONDITION(g_base->InLogicThread()); + auto* dev_console = g_base->ui->dev_console(); + BA_PRECONDITION(dev_console); + BA_PRECONDITION(dev_console->IsActive()); + return PyFloat_FromDouble(dev_console->Width()); + BA_PYTHON_CATCH; +} + +static PyMethodDef PyDevConsoleTabWidthDef = { + "dev_console_tab_width", // name + (PyCFunction)PyDevConsoleTabWidth, // method + METH_NOARGS, // flags + + "dev_console_tab_width() -> float\n" + "\n" + "(internal)", +}; + +// ------------------------ dev_console_tab_height ----------------------------- + +static auto PyDevConsoleTabHeight(PyObject* self) -> PyObject* { + BA_PYTHON_TRY; + BA_PRECONDITION(g_base->InLogicThread()); + auto* dev_console = g_base->ui->dev_console(); + BA_PRECONDITION(dev_console); + BA_PRECONDITION(dev_console->IsActive()); + return PyFloat_FromDouble(dev_console->Height()); + BA_PYTHON_CATCH; +} + +static PyMethodDef PyDevConsoleTabHeightDef = { + "dev_console_tab_height", // name + (PyCFunction)PyDevConsoleTabHeight, // method + METH_NOARGS, // flags + + "dev_console_tab_height() -> float\n" + "\n" + "(internal)", +}; + +// ----------------------- dev_console_base_scale ------------------------------ + +static auto PyDevConsoleBaseScale(PyObject* self) -> PyObject* { + BA_PYTHON_TRY; + BA_PRECONDITION(g_base->InLogicThread()); + auto* dev_console = g_base->ui->dev_console(); + BA_PRECONDITION(dev_console); + BA_PRECONDITION(dev_console->IsActive()); + return PyFloat_FromDouble(dev_console->BaseScale()); + BA_PYTHON_CATCH; +} + +static PyMethodDef PyDevConsoleBaseScaleDef = { + "dev_console_base_scale", // name + (PyCFunction)PyDevConsoleBaseScale, // method + METH_NOARGS, // flags + + "dev_console_base_scale() -> float\n" + "\n" + "(internal)", +}; + +// -------------------- dev_console_request_refresh ---------------------------- + +static auto PyDevConsoleRequestRefresh(PyObject* self) -> PyObject* { + BA_PYTHON_TRY; + BA_PRECONDITION(g_base->InLogicThread()); + auto* dev_console = g_base->ui->dev_console(); + BA_PRECONDITION(dev_console); + BA_PRECONDITION(dev_console->IsActive()); + dev_console->RequestRefresh(); + Py_RETURN_NONE; + BA_PYTHON_CATCH; +} + +static PyMethodDef PyDevConsoleRequestRefreshDef = { + "dev_console_request_refresh", // name + (PyCFunction)PyDevConsoleRequestRefresh, // method + METH_NOARGS, // flags + + "dev_console_request_refresh() -> None\n" + "\n" + "(internal)", +}; + // ----------------------------------------------------------------------------- auto PythonMethodsMisc::GetMethods() -> std::vector { @@ -1505,6 +1669,12 @@ auto PythonMethodsMisc::GetMethods() -> std::vector { PyNativeStackTraceDef, PyOpenDirExternallyDef, PyFatalErrorDef, + PyDevConsoleAddButtonDef, + PyDevConsoleAddPythonTerminalDef, + PyDevConsoleTabWidthDef, + PyDevConsoleTabHeightDef, + PyDevConsoleBaseScaleDef, + PyDevConsoleRequestRefreshDef, }; } diff --git a/src/ballistica/base/ui/dev_console.cc b/src/ballistica/base/ui/dev_console.cc index c5bf0387..fea4f2d9 100644 --- a/src/ballistica/base/ui/dev_console.cc +++ b/src/ballistica/base/ui/dev_console.cc @@ -16,6 +16,7 @@ #include "ballistica/core/core.h" #include "ballistica/core/platform/support/min_sdl.h" #include "ballistica/shared/foundation/event_loop.h" +#include "ballistica/shared/foundation/macros.h" #include "ballistica/shared/generic/utils.h" #include "ballistica/shared/python/python_command.h" #include "ballistica/shared/python/python_sys.h" @@ -103,7 +104,8 @@ class DevConsole::Button_ : public DevConsole::Widget_ { template Button_(const std::string& label, float text_scale, DevButtonAttach_ attach, - float x, float y, float width, float height, const F& lambda) + float x, float y, float width, float height, float corner_radius, + const F& lambda) : attach{attach}, x{x}, y{y}, @@ -112,14 +114,10 @@ class DevConsole::Button_ : public DevConsole::Widget_ { call{NewLambdaRunnable(lambda)}, text_scale{text_scale}, mesh(0.0f, 0.0f, 0.0f, width, height, - NinePatchMesh::BorderForRadius(kDevConsoleButtonCornerRadius, - width, height), - NinePatchMesh::BorderForRadius(kDevConsoleButtonCornerRadius, - height, width), - NinePatchMesh::BorderForRadius(kDevConsoleButtonCornerRadius, - width, height), - NinePatchMesh::BorderForRadius(kDevConsoleButtonCornerRadius, - height, width)) { + NinePatchMesh::BorderForRadius(corner_radius, width, height), + NinePatchMesh::BorderForRadius(corner_radius, height, width), + NinePatchMesh::BorderForRadius(corner_radius, width, height), + NinePatchMesh::BorderForRadius(corner_radius, height, width)) { text_group.SetText(label, TextMesh::HAlign::kCenter, TextMesh::VAlign::kCenter); } @@ -309,9 +307,10 @@ class DevConsole::TabButton_ : public DevConsole::Widget_ { } }; -class DevConsole::Line_ { +class DevConsole::OutputLine_ { public: - Line_(std::string s_in, double c) : creation_time(c), s(std::move(s_in)) {} + OutputLine_(std::string s_in, double c) + : creation_time(c), s(std::move(s_in)) {} double creation_time; std::string s; auto GetText() -> TextGroup& { @@ -340,43 +339,112 @@ DevConsole::DevConsole() { title_text_group_.SetText(title); built_text_group_.SetText("Built: " __DATE__ " " __TIME__); prompt_text_group_.SetText(">"); - - Refresh(); } -void DevConsole::Refresh() { - BA_PRECONDITION(g_base->InLogicThread()); - buttons_.clear(); - tab_buttons_.clear(); - RefreshTabsButtons_(); +DevConsole::~DevConsole() = default; - if (active_tab_ == "Python") { - float bs = PythonConsoleBaseScale_(); - buttons_.emplace_back(std::make_unique( - "Exec", 0.75f * bs, DevButtonAttach_::kRight, -33.0f * bs, 15.95f * bs, - 32.0f * bs, 13.0f * bs, [this] { Exec(); })); +void DevConsole::RefreshTabButtons_() { + // Ask the Python layer for the latest set of tabs. + tabs_ = g_base->python->objs() + .Get(BasePython::ObjID::kGetDevConsoleTabNamesCall) + .Call() + .ValueAsStringSequence(); + // If we have tabs and none of them are selected, select the first. + if (!tabs_.empty()) { + bool found{}; + for (auto&& tab : tabs_) { + if (active_tab_ == tab) { + found = true; + break; + } + } + if (!found) { + active_tab_ = tabs_.front(); + } } -} -void DevConsole::RefreshTabsButtons_() { - float bs = PythonConsoleBaseScale_(); + // Now rebuild our buttons for them. + tab_buttons_.clear(); + float bs = BaseScale(); float bwidth = 90.0f * bs; float bheight = 26.0f * bs; float bscale = 0.8f * bs; float total_width = tabs_.size() * bwidth; float x = total_width * -0.5f; - for (auto&& tab : tabs_) { tab_buttons_.emplace_back(std::make_unique( tab, active_tab_ == tab, bscale, DevButtonAttach_::kCenter, x, -bheight, bwidth, bheight, [this, tab] { active_tab_ = tab; - Refresh(); + RefreshTabButtons_(); + RefreshTabContents_(); })); x += bwidth; } } -DevConsole::~DevConsole() = default; + +void DevConsole::RefreshTabContents_() { + BA_PRECONDITION(g_base->InLogicThread()); + + // Consider any refresh requests fulfilled. Subsequent refresh-requests + // will generate a new refresh at this point. + refresh_pending_ = false; + + // Clear to an empty slate. + buttons_.clear(); + python_terminal_visible_ = false; + + // Now ask the Python layer to fill this tab in. + PythonRef args(Py_BuildValue("(s)", active_tab_.c_str()), PythonRef::kSteal); + g_base->python->objs() + .Get(BasePython::ObjID::kAppDevConsoleDoRefreshTabCall) + .Call(args); +} + +void DevConsole::AddButton(const char* label, float x, float y, float width, + float height, PyObject* call, const char* h_anchor, + float label_scale, float corner_radius) { + assert(g_base->InLogicThread()); + + DevButtonAttach_ anchor; + if (!strcmp(h_anchor, "left")) { + anchor = DevButtonAttach_::kLeft; + } else if (!strcmp(h_anchor, "right")) { + anchor = DevButtonAttach_::kRight; + } else { + assert(!strcmp(h_anchor, "center")); + anchor = DevButtonAttach_::kCenter; + } + + // auto call_obj = PythonRef::Acquired(call); + buttons_.emplace_back(std::make_unique( + label, label_scale, anchor, x, y, width, height, corner_radius, + [this, call_obj = PythonRef::Acquired(call)] { + if (call_obj.Get() != Py_None) { + call_obj.Call(); + } + })); +} + +void DevConsole::AddPythonTerminal() { + float bs = BaseScale(); + buttons_.emplace_back(std::make_unique( + "Exec", 0.75f * bs, DevButtonAttach_::kRight, -33.0f * bs, 15.95f * bs, + 32.0f * bs, 13.0f * bs, 2.0 * bs, [this] { Exec(); })); + python_terminal_visible_ = true; +} + +void DevConsole::RequestRefresh() { + assert(g_base->InLogicThread()); + + // Schedule a refresh. If one is already scheduled but hasn't run, do + // nothing. + if (refresh_pending_) { + return; + } + refresh_pending_ = true; + g_base->logic->event_loop()->PushCall([this] { RefreshTabContents_(); }); +} auto DevConsole::HandleMouseDown(int button, float x, float y) -> bool { assert(g_base->InLogicThread()); @@ -404,13 +472,21 @@ auto DevConsole::HandleMouseDown(int button, float x, float y) -> bool { return false; } - if (button == 1) { - python_console_pressed_ = true; + if (button == 1 && python_terminal_visible_) { + python_terminal_pressed_ = true; } return true; } +auto DevConsole::Width() -> float { + return g_base->graphics->screen_virtual_width(); +} + +auto DevConsole::Height() -> float { + return g_base->graphics->screen_virtual_height() - Bottom_(); +} + void DevConsole::HandleMouseUp(int button, float x, float y) { assert(g_base->InLogicThread()); float bottom{Bottom_()}; @@ -424,8 +500,8 @@ void DevConsole::HandleMouseUp(int button, float x, float y) { } } - if (button == 1 && python_console_pressed_) { - python_console_pressed_ = false; + if (button == 1 && python_terminal_pressed_) { + python_terminal_pressed_ = false; if (y > bottom) { // If we're not getting fed keyboard events and have a string editor // available, invoke it. @@ -548,19 +624,6 @@ auto DevConsole::HandleKeyPress(const SDL_Keysym* keysym) -> bool { break; } default: { - // #if BA_SDL2_BUILD || BA_MINSDL_BUILD - // // (in SDL2/Non-SDL we dont' get chars from keypress events; - // // they come through as text edit events) - // #else // BA_SDL2_BUILD - // if (keysym->unicode < 0x80 && keysym->unicode > 0) { - // std::vector unichars = - // Utils::UnicodeFromUTF8(input_string_, "cjofrh0"); - // unichars.push_back(keysym->unicode); - // input_string_ = Utils::GetValidUTF8( - // Utils::UTF8FromUnicode(unichars).c_str(), "sdkr"); - // input_text_dirty_ = true; - // } - // #endif // BA_SDL2_BUILD break; } } @@ -576,9 +639,9 @@ void DevConsole::Exec() { input_history_position_ = 0; if (input_string_ == "clear") { last_line_.clear(); - lines_.clear(); + output_lines_.clear(); } else { - SubmitCommand_(input_string_); + SubmitPythonCommand_(input_string_); } input_history_.push_front(input_string_); if (input_history_.size() > 100) { @@ -588,7 +651,7 @@ void DevConsole::Exec() { input_text_dirty_ = true; } -void DevConsole::SubmitCommand_(const std::string& command) { +void DevConsole::SubmitPythonCommand_(const std::string& command) { assert(g_base); g_base->logic->event_loop()->PushCall([command, this] { // These are always run in whichever context is 'visible'. @@ -631,9 +694,12 @@ void DevConsole::ToggleState() { switch (state_) { case State_::kInactive: state_ = State_::kMini; + RefreshTabButtons_(); + RefreshTabContents_(); break; case State_::kMini: state_ = State_::kFull; + RefreshTabContents_(); break; case State_::kFull: state_ = State_::kInactive; @@ -650,6 +716,9 @@ auto DevConsole::HandleTextEditing(const std::string& text) -> bool { } // Ignore back-tick because we use that key to toggle the console. + // + // FIXME: Perhaps should allow typing it if some control-character is + // held? if (text == "`") { return false; } @@ -679,9 +748,9 @@ void DevConsole::Print(const std::string& s_in) { // Spit out all completed lines and keep the last one as lastline. for (size_t i = 0; i < broken_up.size() - 1; i++) { - lines_.emplace_back(broken_up[i], g_base->logic->display_time()); - if (lines_.size() > kDevConsoleLineLimit) { - lines_.pop_front(); + output_lines_.emplace_back(broken_up[i], g_base->logic->display_time()); + if (output_lines_.size() > kDevConsoleLineLimit) { + output_lines_.pop_front(); } } last_line_ = broken_up[broken_up.size() - 1]; @@ -689,13 +758,18 @@ void DevConsole::Print(const std::string& s_in) { } auto DevConsole::Bottom_() const -> float { - float bs = PythonConsoleBaseScale_(); float vh = g_base->graphics->screen_virtual_height(); float ratio = (g_base->logic->display_time() - transition_start_) / kTransitionSeconds; float bottom; - float mini_size = 90.0f * bs; + + // NOTE: Originally I was tweaking this based on UI scale, but I decided + // that it would be a better idea to have a constant value everywhere. + // dev-consoles are not meant to be especially pretty and I think it is + // more important for them to be able to be written to a known hard-coded + // mini-size. + float mini_size = 100.0f; if (state_ == State_::kMini) { bottom = vh - mini_size; } else { @@ -724,7 +798,7 @@ auto DevConsole::Bottom_() const -> float { } void DevConsole::Draw(FrameDef* frame_def) { - float bs = PythonConsoleBaseScale_(); + float bs = BaseScale(); RenderPass* pass = frame_def->overlay_front_pass(); // If we're not yet transitioning in for the first time OR have completed @@ -753,7 +827,7 @@ void DevConsole::Draw(FrameDef* frame_def) { c.SetColor(0, 0, 0.1f, 0.9f); c.DrawMesh(&bg_mesh_); c.Submit(); - if (active_tab_ == "Python") { + if (python_terminal_visible_) { c.SetColor(1.0f, 1.0f, 1.0f, 0.1f); c.DrawMesh(&stripe_mesh_); c.Submit(); @@ -779,7 +853,7 @@ void DevConsole::Draw(FrameDef* frame_def) { } } - if (active_tab_ == "Python") { + if (python_terminal_visible_) { if (input_text_dirty_) { input_text_group_.SetText(input_string_); input_text_dirty_ = false; @@ -887,7 +961,7 @@ void DevConsole::Draw(FrameDef* frame_def) { } v += v_inc; } - for (auto i = lines_.rbegin(); i != lines_.rend(); i++) { + for (auto i = output_lines_.rbegin(); i != output_lines_.rend(); i++) { int elem_count = i->GetText().GetElementCount(); for (int e = 0; e < elem_count; e++) { c.SetTexture(i->GetText().GetElementTexture(e)); @@ -922,7 +996,7 @@ void DevConsole::Draw(FrameDef* frame_def) { } } -auto DevConsole::PythonConsoleBaseScale_() const -> float { +auto DevConsole::BaseScale() const -> float { switch (g_base->ui->scale()) { case UIScale::kLarge: return 1.5f; diff --git a/src/ballistica/base/ui/dev_console.h b/src/ballistica/base/ui/dev_console.h index a3881107..e13c1d33 100644 --- a/src/ballistica/base/ui/dev_console.h +++ b/src/ballistica/base/ui/dev_console.h @@ -51,24 +51,42 @@ class DevConsole { auto HandleMouseDown(int button, float x, float y) -> bool; void HandleMouseUp(int button, float x, float y); void Exec(); - void Refresh(); + + void AddButton(const char* label, float x, float y, float width, float height, + PyObject* call, const char* h_anchor, float label_scale, + float corner_radius); + void AddPythonTerminal(); + + auto Width() -> float; + auto Height() -> float; + auto BaseScale() const -> float; + void RequestRefresh(); private: class Widget_; class Button_; class ToggleButton_; class TabButton_; - class Line_; - enum class State_ { kInactive, kMini, kFull }; + class OutputLine_; + enum class State_ : uint8_t { kInactive, kMini, kFull }; auto Bottom_() const -> float; - auto PythonConsoleBaseScale_() const -> float; - void SubmitCommand_(const std::string& command); + void SubmitPythonCommand_(const std::string& command); void InvokeStringEditor_(); - void RefreshTabsButtons_(); + void RefreshTabButtons_(); + void RefreshTabContents_(); - std::list tabs_{"Python", "AppModes", "Logging", "Graphics"}; - std::string active_tab_{"Python"}; + 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_{}; + 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_; @@ -76,21 +94,15 @@ class DevConsole { TextGroup title_text_group_; TextGroup prompt_text_group_; TextGroup input_text_group_; - millisecs_t last_input_text_change_time_{}; - bool input_text_dirty_{true}; - double transition_start_{}; - State_ state_{State_::kInactive}; - State_ state_prev_{State_::kInactive}; - bool input_enabled_{}; - std::string input_string_; - std::list input_history_; - int input_history_position_{}; - std::list lines_; std::string last_line_; - Object::Ref last_line_mesh_group_; - bool last_line_mesh_dirty_{true}; - bool python_console_pressed_{}; + std::string input_string_; + std::list tabs_{"Python", "AppModes", "Logging", "Graphics", + "UI"}; + std::string active_tab_{"Python"}; PythonRef string_edit_adapter_; + Object::Ref last_line_mesh_group_; + std::list input_history_; + std::list output_lines_; std::vector > buttons_; std::vector > tab_buttons_; }; diff --git a/src/ballistica/shared/ballistica.cc b/src/ballistica/shared/ballistica.cc index 71b6cc9e..737c37d2 100644 --- a/src/ballistica/shared/ballistica.cc +++ b/src/ballistica/shared/ballistica.cc @@ -39,7 +39,7 @@ auto main(int argc, char** argv) -> int { namespace ballistica { // These are set automatically via script; don't modify them here. -const int kEngineBuildNumber = 21401; +const int kEngineBuildNumber = 21405; const char* kEngineVersion = "1.7.28"; const int kEngineApiVersion = 8; diff --git a/src/ballistica/shared/buildconfig/buildconfig_cmake.h b/src/ballistica/shared/buildconfig/buildconfig_cmake.h index f037f68a..d8a338eb 100644 --- a/src/ballistica/shared/buildconfig/buildconfig_cmake.h +++ b/src/ballistica/shared/buildconfig/buildconfig_cmake.h @@ -21,7 +21,7 @@ #endif #define BA_OSTYPE_MACOS 1 -#define BA_HAVE_FRAMEWORK_OPENAL 1 +// #define BA_HAVE_FRAMEWORK_OPENAL 1 #elif __linux__ diff --git a/src/ballistica/shared/python/python.cc b/src/ballistica/shared/python/python.cc index d700538e..e35ca919 100644 --- a/src/ballistica/shared/python/python.cc +++ b/src/ballistica/shared/python/python.cc @@ -225,7 +225,7 @@ auto Python::GetPyFloats(PyObject* o) -> std::vector { return vals; } -auto Python::GetPyStrings(PyObject* o) -> std::list { +auto Python::GetPyStringSequence(PyObject* o) -> std::list { assert(HaveGIL()); BA_PRECONDITION_FATAL(o != nullptr); diff --git a/src/ballistica/shared/python/python.h b/src/ballistica/shared/python/python.h index f8968209..2a94c9e3 100644 --- a/src/ballistica/shared/python/python.h +++ b/src/ballistica/shared/python/python.h @@ -125,7 +125,7 @@ class Python { static auto GetPyInts(PyObject* o) -> std::vector; static auto GetPyUInts64(PyObject* o) -> std::vector; static auto GetPyPoint2D(PyObject* o) -> Point2D; - static auto GetPyStrings(PyObject* o) -> std::list; + static auto GetPyStringSequence(PyObject* o) -> std::list; /// Set Python exception from C++ Exception. static void SetPythonException(const Exception& exc); diff --git a/src/ballistica/shared/python/python_ref.cc b/src/ballistica/shared/python/python_ref.cc index 133ec6d5..e57a9aa4 100644 --- a/src/ballistica/shared/python/python_ref.cc +++ b/src/ballistica/shared/python/python_ref.cc @@ -162,6 +162,12 @@ auto PythonRef::ValueAsString() const -> std::string { return Python::GetPyString(obj_); } +auto PythonRef::ValueAsStringSequence() const -> std::list { + assert(Python::HaveGIL()); + ThrowIfUnset(); + return Python::GetPyStringSequence(obj_); +} + auto PythonRef::ValueAsOptionalInt() const -> std::optional { assert(Python::HaveGIL()); ThrowIfUnset(); @@ -193,7 +199,7 @@ auto PythonRef::ValueAsOptionalStringSequence() const if (obj_ == Py_None) { return {}; } - return Python::GetPyStrings(obj_); + return Python::GetPyStringSequence(obj_); } auto PythonRef::ValueAsInt() const -> int64_t { diff --git a/src/ballistica/shared/python/python_ref.h b/src/ballistica/shared/python/python_ref.h index e0628fda..bdcf020b 100644 --- a/src/ballistica/shared/python/python_ref.h +++ b/src/ballistica/shared/python/python_ref.h @@ -166,6 +166,7 @@ class PythonRef { auto ValueAsLString() const -> std::string; auto ValueAsString() const -> std::string; + auto ValueAsStringSequence() const -> std::list; auto ValueAsOptionalString() const -> std::optional; auto ValueAsOptionalStringSequence() const -> std::optional>; diff --git a/src/meta/babasemeta/pyembed/binding_base.py b/src/meta/babasemeta/pyembed/binding_base.py index 0b85ad93..c5d15011 100644 --- a/src/meta/babasemeta/pyembed/binding_base.py +++ b/src/meta/babasemeta/pyembed/binding_base.py @@ -53,6 +53,7 @@ values = [ _hooks.do_quit, # kQuitCall _hooks.show_post_purchase_message, # kShowPostPurchaseMessageCall _hooks.string_edit_adapter_can_be_replaced, # kStringEditAdapterCanBeReplacedCall + _hooks.get_dev_console_tab_names, # kGetDevConsoleTabNamesCall _language.Lstr, # kLStrClass _general.Call, # kCallClass _apputils.garbage_collect_session_end, # kGarbageCollectSessionEndCall diff --git a/src/meta/babasemeta/pyembed/binding_base_app.py b/src/meta/babasemeta/pyembed/binding_base_app.py index a5d30bad..7248a778 100644 --- a/src/meta/babasemeta/pyembed/binding_base_app.py +++ b/src/meta/babasemeta/pyembed/binding_base_app.py @@ -19,4 +19,5 @@ values = [ app.on_native_shutdown, # kAppOnNativeShutdownCall app.on_native_shutdown_complete, # kAppOnNativeShutdownCompleteCall app.read_config, # kAppReadConfigCall + app.devconsole.do_refresh_tab, # kAppDevConsoleDoRefreshTabCall ] diff --git a/tools/efro/cloudshell.py b/tools/efro/cloudshell.py index e2f269bd..b3c1cf68 100644 --- a/tools/efro/cloudshell.py +++ b/tools/efro/cloudshell.py @@ -36,7 +36,6 @@ class HostConfig: mosh_shell: str = 'sh' workspaces_root: str = '/home/${USER}/cloudshell_workspaces' sync_perms: bool = True - precommand: str | None = None # KILL THIS precommand_noninteractive: str | None = None precommand_interactive: str | None = None managed: bool = False diff --git a/tools/efrotools/pcommands.py b/tools/efrotools/pcommands.py index 547d9491..207befb0 100644 --- a/tools/efrotools/pcommands.py +++ b/tools/efrotools/pcommands.py @@ -728,16 +728,18 @@ def echo() -> None: clrnames = {n for n in dir(clr) if n.isupper() and not n.startswith('_')} first = True out: list[str] = [] + last_was_tag = False for arg in pcommand.get_args(): if arg in clrnames: out.append(getattr(clr, arg)) + last_was_tag = True else: - # Special case: a dot by itself is treated as a period for - # the previous arg. This lets us do periods following color - # tags. - if not first and arg != '.': + # Special case: punctuation by itself after a tag doesn't + # get a space before it. + if not first and not (last_was_tag and arg in ('.', '?', '!')): out.append(' ') first = False + last_was_tag = False out.append(arg) out.append(clr.RST) pcommand.clientprint(''.join(out))