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