diff --git a/.efrocachemap b/.efrocachemap index 665bb33f..2466070f 100644 --- a/.efrocachemap +++ b/.efrocachemap @@ -421,7 +421,7 @@ "build/assets/ba_data/audio/zoeOw.ogg": "74befe45a8417e95b6a2233c51992a26", "build/assets/ba_data/audio/zoePickup01.ogg": "48ab8cddfcde36a750856f3f81dd20c8", "build/assets/ba_data/audio/zoeScream01.ogg": "2b468aedfa8741090247f04eb9e6df55", - "build/assets/ba_data/data/langdata.json": "992c5c5ce292132c4f011f39e0d13de8", + "build/assets/ba_data/data/langdata.json": "d21a1b7c9444ae7787944498f3ed6482", "build/assets/ba_data/data/languages/arabic.json": "d1f900ab5aa2433d402bd46ed1149cc7", "build/assets/ba_data/data/languages/belarussian.json": "e151808b6b4f6dc159cf55ee62adad3c", "build/assets/ba_data/data/languages/chinese.json": "8d889accdd49334591209bdaf6eaf02f", @@ -430,12 +430,12 @@ "build/assets/ba_data/data/languages/czech.json": "93c5fe0d884d95435da6c675f64e30e0", "build/assets/ba_data/data/languages/danish.json": "3fd69080783d5c9dcc0af737f02b6f1e", "build/assets/ba_data/data/languages/dutch.json": "22b44a33bf81142ba2befad14eb5746e", - "build/assets/ba_data/data/languages/english.json": "b38d54aecf3ac47b8d8ca97d8bab3006", + "build/assets/ba_data/data/languages/english.json": "e70277fc6325126d3d893524c8df03c9", "build/assets/ba_data/data/languages/esperanto.json": "0e397cfa5f3fb8cef5f4a64f21cda880", "build/assets/ba_data/data/languages/filipino.json": "347f38524816691170d266708fe25894", "build/assets/ba_data/data/languages/french.json": "d8527da977a563185de25ef02bacf826", - "build/assets/ba_data/data/languages/german.json": "549754d2a530d825200c6126be56df5c", - "build/assets/ba_data/data/languages/gibberish.json": "837423db378b3e7679683805826aa26e", + "build/assets/ba_data/data/languages/german.json": "450fa41ae264f29a5d1af22143d0d0ad", + "build/assets/ba_data/data/languages/gibberish.json": "7863ceeedb1e87eef46f7769bae5f842", "build/assets/ba_data/data/languages/greek.json": "a65d78f912e9a89f98de004405167a6a", "build/assets/ba_data/data/languages/hindi.json": "88ee0cda537bab9ac827def5e236fe1a", "build/assets/ba_data/data/languages/hungarian.json": "796a290a8c44a1e7635208c2ff5fdc6e", @@ -447,12 +447,12 @@ "build/assets/ba_data/data/languages/polish.json": "e1a1a801851924748ad38fa68216439a", "build/assets/ba_data/data/languages/portuguese.json": "9fcd6b4da9e5d0dc0e337ab00b5debe2", "build/assets/ba_data/data/languages/romanian.json": "aeebdd54f65939c2facc6ac50c117826", - "build/assets/ba_data/data/languages/russian.json": "910cf653497654a16d5c4f067d6def22", + "build/assets/ba_data/data/languages/russian.json": "7f1689fff58321fdaa632cb9c45105df", "build/assets/ba_data/data/languages/serbian.json": "d7452dd72ac0e51680cb39b5ebaa1c69", "build/assets/ba_data/data/languages/slovak.json": "27962d53dc3f7dd4e877cd40faafeeef", "build/assets/ba_data/data/languages/spanish.json": "0122b0b24aa111ab259af02bbae9b7b6", "build/assets/ba_data/data/languages/swedish.json": "77d671f10613291ebf9c71da66f18a18", - "build/assets/ba_data/data/languages/tamil.json": "b9d4b4e107456ea6420ee0f9d9d7a03e", + "build/assets/ba_data/data/languages/tamil.json": "65ab7798d637fa62a703750179eeb723", "build/assets/ba_data/data/languages/thai.json": "33f63753c9af9a5b238d229a0bf23fbc", "build/assets/ba_data/data/languages/turkish.json": "9d7e58c9062dc517c3779c255a9b3142", "build/assets/ba_data/data/languages/ukrainian.json": "f72eb51abfbbb56e27866895d7e947d2", @@ -4056,53 +4056,53 @@ "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": "8ea626f6dd70d998ee77c58fffc51545", - "build/prefab/full/linux_arm64_gui/release/ballisticakit": "d7ad10904fe7c4d4555366ccb1feedcb", - "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "a9eea92d521e97b1772b5e44b402ce8a", - "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "57043f40971d800d27ee6d646aae8d61", - "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "3e4009d7fa4b90abc526f56361ecab05", - "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "334cdc0688e66a1bc75cd05bae1729c7", - "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "95278d80378be5b61026253492cbfa70", - "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "0a3da8e5264a7b733960e83a0e8c4bba", - "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "a089d4aaa39e18553cf0a70a77b4cfcd", - "build/prefab/full/mac_arm64_gui/release/ballisticakit": "e66c4eceb79710b8fda2bfea781e241a", - "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "6b3021b9a7584da86bbb95324e81e851", - "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "6fe90d50f905a0da9fa52c39a458d1e3", - "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "afc7ed826486aec82613832865177570", - "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "ae8b6dad770793188aad8e2966189bb3", - "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "7622dde1a021152cac42e7db3e803392", - "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "8a4b1e521bf668cc6ec6a65519defb12", - "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "9a69c0d4c9ae319595843b16f14795fc", - "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "89c02260fb4781f5e293658cecbb363f", - "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "3165d4230069c22300abfff8abf1d714", - "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "41e46dfdbb542e3e823f6aee87e93ac9", - "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "20cc128ff9d44f9d74e4301c6d49f48f", - "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "f93bc8f98ee31f39b54ab46264eccb22", - "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "20cc128ff9d44f9d74e4301c6d49f48f", - "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "f93bc8f98ee31f39b54ab46264eccb22", - "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "1565f3b227843827d692cb3ef65847b6", - "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "d8d74c6c40db43054ccc7d27920cfbfe", - "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "1565f3b227843827d692cb3ef65847b6", - "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "d8d74c6c40db43054ccc7d27920cfbfe", - "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "43bdfa8acd84e9cf2e443ce8e923c229", - "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "d935dc21becdfd65bec51c5f5b2fd770", - "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "43bdfa8acd84e9cf2e443ce8e923c229", - "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "d935dc21becdfd65bec51c5f5b2fd770", - "build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "6d49ad39f194480da458b431405f5a2b", - "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "c8715c85010ea431d7346f40f5421819", - "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "49775819d4ba9af15061080d17377a18", - "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "c8715c85010ea431d7346f40f5421819", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "750c2964308cd3f3e5986fcda9a25706", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "772af7da6a115f53b0b3f6a4afd3baec", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "178c1a53a7ad50297aed68d0ca3a1476", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "b011bc2b6437995f2d33f5215b4ffa36", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "a758a4f98336208381b093aacb735878", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "6c86545fab2327105114676a20ca5e68", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "f75525fdc9f7db4a81ca9bae6a79add5", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "2e297069baec404d43ccdb18abeef658", + "build/prefab/full/linux_arm64_gui/debug/ballisticakit": "05edee29f50c1d6bee9d3de307248ea6", + "build/prefab/full/linux_arm64_gui/release/ballisticakit": "df3990491fab9ac0538e75f347683a94", + "build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "a7efd60daa1a52b0e30a7a9616cc727a", + "build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "2b964e4df219ccf31d204c701abf8b6e", + "build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "67845ed4ac3307559448f9f323e3408f", + "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "b5934ad96454b3fb945a388141166f17", + "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "cfec3ecd22fa9467a65f8878efa2b911", + "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "e748d9e8a1f9b9287194f03a5a9f66d2", + "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "f3897d23722ab9cf60614dea1abace10", + "build/prefab/full/mac_arm64_gui/release/ballisticakit": "74881b34024a827de91dc4e302123548", + "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "82b5a5410419a738ffb1007f736644c2", + "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "f732999c2601cf656b850f5f3e9570f4", + "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "0da70003570c3f392a0f87a823c2adc3", + "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "1535ddde4b8532a412df37567a1a29a3", + "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "e04124a9110037eabe1aac45a98891db", + "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "c07e02b94aa9f50f21bb6571d3a2d5e5", + "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "ce594cf55f33b80664e5e2c9d59a4ba1", + "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "26e0fec05e2813fa3e41aa36e3b43df3", + "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "3957607929916c7ea7f8cde6487e2101", + "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "96680b806f2997e75904b4222a1e0536", + "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "80ff019ab0715f68dc4cae15c7a9fdab", + "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "bbe53e6685130822cbba276f63b76dcb", + "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "80ff019ab0715f68dc4cae15c7a9fdab", + "build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "bbe53e6685130822cbba276f63b76dcb", + "build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "0d0737d71a7efe201e5ebadeb5f12c71", + "build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "895099e15bad328666f71869f6f09b79", + "build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "0d0737d71a7efe201e5ebadeb5f12c71", + "build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "895099e15bad328666f71869f6f09b79", + "build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "79e38e7d6eb4a5810bbb22e94d4e87ee", + "build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "2cf7ef381bc7408c21f4ea444815233e", + "build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "79e38e7d6eb4a5810bbb22e94d4e87ee", + "build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "2cf7ef381bc7408c21f4ea444815233e", + "build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "5d457e3b58015c9da26a7911a6c8ed5a", + "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "70d5ec6b86c91c787a182da729737951", + "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "444861c2242f3f0b9e09046b6cbf0245", + "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "70d5ec6b86c91c787a182da729737951", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "4c37ef02c9d97ebf58e9cd4055b557a7", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "5450d7efd2aec4481be61a1315d78af2", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "42a5fefe21fdffdfb62a0f236b33193e", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "d8f2d5d50cfedfff5b1ce1c9c7c52ef4", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "f19d0d8db9b776291dc3bc99f402608e", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "c1dea4974ce3723ded4391eca5ae2bfc", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "cf3bbe5f15e76197937e67cfaf2f0a32", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "2b8c93e04d1876a787b2a8befbbde004", "src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c", "src/assets/ba_data/python/babase/_mgen/enums.py": "28323912b56ec07701eda3d41a6a4101", - "src/ballistica/base/mgen/pyembed/binding_base.inc": "ba8ce3ca3858b4c2d20db68f99b788b2", + "src/ballistica/base/mgen/pyembed/binding_base.inc": "bb96031e3f844704fcc9a0549a6d2c41", "src/ballistica/base/mgen/pyembed/binding_base_app.inc": "00f81f9bd92386ec12a6e60170678a98", "src/ballistica/classic/mgen/pyembed/binding_classic.inc": "3ceb412513963f0818ab39c58bf292e3", "src/ballistica/core/mgen/pyembed/binding_core.inc": "9d0a3c9636138e35284923e0c8311c69", diff --git a/CHANGELOG.md b/CHANGELOG.md index 21aade8c..fd8a507c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -### 1.7.28 (build 21465, api 8, 2023-10-14) +### 1.7.28 (build 21468, api 8, 2023-10-16) - 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 @@ -146,7 +146,8 @@ rates are high. - Added a proper graceful shutdown process for the audio server. This should result in fewer ugly pops and warning messages when the app is quit. - +- Tidied up some keyboard shortcuts to be more platform-appropriate. For + example, toggling fullscreen on Windows is now Alt+Enter or F11. ### 1.7.27 (build 21282, api 8, 2023-08-30) diff --git a/src/assets/ba_data/python/babase/__init__.py b/src/assets/ba_data/python/babase/__init__.py index fa542c74..9eb09af0 100644 --- a/src/assets/ba_data/python/babase/__init__.py +++ b/src/assets/ba_data/python/babase/__init__.py @@ -27,7 +27,10 @@ from _babase import ( apptime, apptimer, AppTimer, - can_toggle_fullscreen, + fullscreen_control_available, + fullscreen_control_get, + fullscreen_control_key_shortcut, + fullscreen_control_set, charstr, clipboard_get_text, clipboard_has_text, @@ -200,7 +203,10 @@ __all__ = [ 'apptimer', 'AppTimer', 'Call', - 'can_toggle_fullscreen', + 'fullscreen_control_available', + 'fullscreen_control_get', + 'fullscreen_control_key_shortcut', + 'fullscreen_control_set', 'charstr', 'clipboard_get_text', 'clipboard_has_text', diff --git a/src/assets/ba_data/python/babase/_app.py b/src/assets/ba_data/python/babase/_app.py index 84147e5d..ffd1f05d 100644 --- a/src/assets/ba_data/python/babase/_app.py +++ b/src/assets/ba_data/python/babase/_app.py @@ -895,10 +895,18 @@ class App: import asyncio # Spin and wait for anything blocking shutdown to complete. + starttime = _babase.apptime() _babase.lifecyclelog('shutdown-suppress wait begin') while _babase.shutdown_suppress_count() > 0: await asyncio.sleep(0.001) _babase.lifecyclelog('shutdown-suppress wait end') + duration = _babase.apptime() - starttime + if duration > 1.0: + logging.warning( + 'Shutdown-suppressions delayed shutdown longer than ideal ' + '(%.2f seconds).', + duration, + ) async def _fade_and_shutdown_graphics(self) -> None: import asyncio diff --git a/src/assets/ba_data/python/babase/_hooks.py b/src/assets/ba_data/python/babase/_hooks.py index 90dde9be..69881e96 100644 --- a/src/assets/ba_data/python/babase/_hooks.py +++ b/src/assets/ba_data/python/babase/_hooks.py @@ -33,18 +33,30 @@ def reset_to_main_menu() -> None: logging.warning('reset_to_main_menu: no-op due to classic not present.') -def set_config_fullscreen_on() -> None: +def store_config_fullscreen_on() -> None: """The OS has changed our fullscreen state and we should take note.""" _babase.app.config['Fullscreen'] = True _babase.app.config.commit() -def set_config_fullscreen_off() -> None: +def store_config_fullscreen_off() -> None: """The OS has changed our fullscreen state and we should take note.""" _babase.app.config['Fullscreen'] = False _babase.app.config.commit() +def set_config_fullscreen_on() -> None: + """Set and store fullscreen state""" + _babase.app.config['Fullscreen'] = True + _babase.app.config.apply_and_commit() + + +def set_config_fullscreen_off() -> None: + """The OS has changed our fullscreen state and we should take note.""" + _babase.app.config['Fullscreen'] = False + _babase.app.config.apply_and_commit() + + def not_signed_in_screen_message() -> None: from babase._language import Lstr diff --git a/src/assets/ba_data/python/baenv.py b/src/assets/ba_data/python/baenv.py index b832f68b..16de717b 100644 --- a/src/assets/ba_data/python/baenv.py +++ b/src/assets/ba_data/python/baenv.py @@ -40,7 +40,7 @@ if TYPE_CHECKING: # the last load. Either way, however, multiple execs will happen in some # form. # -# So we need to do a few things to handle that situation gracefully. +# To handle that situation gracefully, we need to do a few things: # # - First, we need to store any mutable global state in the __main__ # module; not in ourself. This way, alternate versions of ourself will @@ -48,11 +48,11 @@ if TYPE_CHECKING: # # - Second, we should avoid the use of isinstance and similar calls for # our types. An EnvConfig we create would technically be a different -# type than that created by an alternate baenv. +# type than an EnvConfig created by an alternate baenv. # Build number and version of the ballistica binary we expect to be # using. -TARGET_BALLISTICA_BUILD = 21465 +TARGET_BALLISTICA_BUILD = 21468 TARGET_BALLISTICA_VERSION = '1.7.28' diff --git a/src/assets/ba_data/python/bauiv1/__init__.py b/src/assets/ba_data/python/bauiv1/__init__.py index 3d3a7b42..535d6719 100644 --- a/src/assets/ba_data/python/bauiv1/__init__.py +++ b/src/assets/ba_data/python/bauiv1/__init__.py @@ -31,7 +31,10 @@ from babase import ( apptimer, AppTimer, Call, - can_toggle_fullscreen, + fullscreen_control_available, + fullscreen_control_get, + fullscreen_control_key_shortcut, + fullscreen_control_set, charstr, clipboard_is_supported, clipboard_set_text, @@ -139,7 +142,10 @@ __all__ = [ 'buttonwidget', 'Call', 'can_show_ad', - 'can_toggle_fullscreen', + 'fullscreen_control_available', + 'fullscreen_control_get', + 'fullscreen_control_key_shortcut', + 'fullscreen_control_set', 'charstr', 'checkboxwidget', 'clipboard_is_supported', diff --git a/src/assets/ba_data/python/bauiv1lib/settings/graphics.py b/src/assets/ba_data/python/bauiv1lib/settings/graphics.py index ec6879b4..1c235095 100644 --- a/src/assets/ba_data/python/bauiv1lib/settings/graphics.py +++ b/src/assets/ba_data/python/bauiv1lib/settings/graphics.py @@ -52,7 +52,7 @@ class GraphicsSettingsWindow(bui.Window): self._show_fullscreen = False fullscreen_spacing_top = spacing * 0.2 fullscreen_spacing = spacing * 1.2 - if bui.can_toggle_fullscreen(): + if bui.fullscreen_control_available(): self._show_fullscreen = True height += fullscreen_spacing + fullscreen_spacing_top @@ -122,21 +122,29 @@ class GraphicsSettingsWindow(bui.Window): self._fullscreen_checkbox: bui.Widget | None = None if self._show_fullscreen: v -= fullscreen_spacing_top - self._fullscreen_checkbox = ConfigCheckBox( + # Fullscreen control does not necessarily talk to the + # app config so we have to wrangle it manually instead of + # using a config-checkbox. + label = bui.Lstr(resource=f'{self._r}.fullScreenText') + + # Show keyboard shortcut alongside the control if they + # provide one. + shortcut = bui.fullscreen_control_key_shortcut() + if shortcut is not None: + label = bui.Lstr( + value='$(NAME) [$(SHORTCUT)]', + subs=[('$(NAME)', label), ('$(SHORTCUT)', shortcut)], + ) + self._fullscreen_checkbox = bui.checkboxwidget( parent=self._root_widget, position=(100, v), - maxwidth=200, + value=bui.fullscreen_control_get(), + on_value_change_call=bui.fullscreen_control_set, + maxwidth=250, size=(300, 30), - configkey='Fullscreen', - displayname=bui.Lstr( - resource=self._r - + ( - '.fullScreenCmdText' - if app.classic.platform == 'mac' - else '.fullScreenCtrlText' - ) - ), - ).widget + text=label, + ) + if not self._have_selected_child: bui.containerwidget( edit=self._root_widget, @@ -528,8 +536,10 @@ class GraphicsSettingsWindow(bui.Window): and bui.apptime() - self._last_max_fps_set_time > 1.0 ): self._apply_max_fps() + if self._show_fullscreen: + # Keep the fullscreen checkbox up to date with the current value. bui.checkboxwidget( edit=self._fullscreen_checkbox, - value=bui.app.config.resolve('Fullscreen'), + value=bui.fullscreen_control_get(), ) diff --git a/src/ballistica/base/app_adapter/app_adapter.cc b/src/ballistica/base/app_adapter/app_adapter.cc index f7fb480c..80391e90 100644 --- a/src/ballistica/base/app_adapter/app_adapter.cc +++ b/src/ballistica/base/app_adapter/app_adapter.cc @@ -15,6 +15,8 @@ #include "ballistica/base/networking/network_reader.h" #include "ballistica/base/networking/networking.h" #include "ballistica/base/platform/base_platform.h" +#include "ballistica/base/python/base_python.h" +#include "ballistica/base/support/app_config.h" #include "ballistica/base/support/stress_test.h" #include "ballistica/base/ui/ui.h" #include "ballistica/shared/foundation/event_loop.h" @@ -239,7 +241,7 @@ void AppAdapter::DoExitMainThreadEventLoop() { FatalError("DoExitMainThreadEventLoop is not implemented here."); } -auto AppAdapter::CanToggleFullscreen() -> bool const { return false; } +auto AppAdapter::FullscreenControlAvailable() const -> bool { return false; } auto AppAdapter::SupportsVSync() -> bool const { return false; } @@ -253,6 +255,29 @@ void AppAdapter::DoPushGraphicsContextRunnable(Runnable* runnable) { DoPushMainThreadRunnable(runnable); } +auto AppAdapter::FullscreenControlGet() const -> bool { + assert(g_base->InLogicThread()); + + // By default, just go through config (assume we have full control over + // the fullscreen state ourself). + return g_base->app_config->Resolve(AppConfig::BoolID::kFullscreen); +} + +void AppAdapter::FullscreenControlSet(bool fullscreen) { + assert(g_base->InLogicThread()); + // By default, just set these in the config and apply it (assumes config + // changes get plugged into actual fullscreen state). + g_base->python->objs() + .Get(fullscreen ? BasePython::ObjID::kSetConfigFullscreenOnCall + : BasePython::ObjID::kSetConfigFullscreenOffCall) + .Call(); +} + +auto AppAdapter::FullscreenControlKeyShortcut() const + -> std::optional { + return {}; +} + void AppAdapter::CursorPositionForDraw(float* x, float* y) { assert(x && y); @@ -271,9 +296,7 @@ auto AppAdapter::ShouldUseCursor() -> bool { return true; } auto AppAdapter::HasHardwareCursor() -> bool { return false; } -void AppAdapter::SetHardwareCursorVisible(bool visible) { - printf("SHOULD SET VIS %d\n", static_cast(visible)); -} +void AppAdapter::SetHardwareCursorVisible(bool visible) {} auto AppAdapter::CanSoftQuit() -> bool { return false; } auto AppAdapter::CanBackQuit() -> bool { return false; } diff --git a/src/ballistica/base/app_adapter/app_adapter.h b/src/ballistica/base/app_adapter/app_adapter.h index 427f536c..7b6339d9 100644 --- a/src/ballistica/base/app_adapter/app_adapter.h +++ b/src/ballistica/base/app_adapter/app_adapter.h @@ -113,9 +113,27 @@ class AppAdapter { auto app_suspended() const { return app_suspended_; } /// Return whether this AppAdapter supports a 'fullscreen' toggle for its - /// display. This currently will simply affect whether that option is - /// available in display settings or via a hotkey. - virtual auto CanToggleFullscreen() -> bool const; + /// display. This will affect whether that option is available in display + /// settings or via a hotkey. Must be called from the logic thread. + virtual auto FullscreenControlAvailable() const -> bool; + + /// AppAdapters supporting a 'fullscreen' control should return the + /// current fullscreen state here. By default this simply returns the + /// app-config fullscreen value (so assumes the actual state is synced to + /// that). Must be called from the logic thread. + virtual auto FullscreenControlGet() const -> bool; + + /// AppAdapters supporting a 'fullscreen' control should set the + /// current fullscreen state here. By default this simply sets the + /// app-config fullscreen value (so assumes the actual state is synced to + /// that). Must be called from the logic thread. + virtual void FullscreenControlSet(bool fullscreen); + + /// AppAdapters supporting a 'fullscreen' control can return a key name + /// here to display if they support toggling via key ('ctrl-F', etc.). + /// Must be called from the logic thread. + virtual auto FullscreenControlKeyShortcut() const + -> std::optional; /// Return whether this AppAdapter supports vsync controls for its display. virtual auto SupportsVSync() -> bool const; diff --git a/src/ballistica/base/app_adapter/app_adapter_sdl.cc b/src/ballistica/base/app_adapter/app_adapter_sdl.cc index 75443228..30c03ee4 100644 --- a/src/ballistica/base/app_adapter/app_adapter_sdl.cc +++ b/src/ballistica/base/app_adapter/app_adapter_sdl.cc @@ -363,8 +363,10 @@ void AppAdapterSDL::HandleSDLEvent_(const SDL_Event& event) { // If they hit the window close button, skip the confirm. g_base->QuitApp(false); } else { - // By default, confirm before quitting. - g_base->QuitApp(true); + // For all other quits we might want to default to a confirm dialog. + // Update: going to try without confirm for a bit and see how that + // feels. + g_base->QuitApp(false); } break; @@ -394,7 +396,7 @@ void AppAdapterSDL::HandleSDLEvent_(const SDL_Event& event) { fullscreen_ = true; g_base->logic->event_loop()->PushCall([] { g_base->python->objs() - .Get(BasePython::ObjID::kSetConfigFullscreenOnCall) + .Get(BasePython::ObjID::kStoreConfigFullscreenOnCall) .Call(); }); } @@ -407,7 +409,7 @@ void AppAdapterSDL::HandleSDLEvent_(const SDL_Event& event) { fullscreen_ = false; g_base->logic->event_loop()->PushCall([] { g_base->python->objs() - .Get(BasePython::ObjID::kSetConfigFullscreenOffCall) + .Get(BasePython::ObjID::kStoreConfigFullscreenOffCall) .Call(); }); } @@ -818,7 +820,28 @@ void AppAdapterSDL::CursorPositionForDraw(float* x, float* y) { *y = immediate_y; } -auto AppAdapterSDL::CanToggleFullscreen() -> bool const { return true; } +auto AppAdapterSDL::FullscreenControlAvailable() const -> bool { return true; } +auto AppAdapterSDL::FullscreenControlKeyShortcut() const + -> std::optional { + if (g_buildconfig.ostype_windows()) { + // On Windows we support F11 and Alt+Enter to toggle fullscreen. Let's + // mention Alt+Enter which seems like it might be more commonly used + return "Alt+Enter"; + } + if (g_buildconfig.ostype_macos()) { + // The Mac+SDL situation is a bit of a mess. By default, there is 'Enter + // Full Screen' in the window menu which is mapped to fn-F, but that + // will only work if a window was created in SDL as windowed. If we + // fullscreen that window and restart the app, we'll then have a *real* + // fullscreen sdl window and that shortcut won't work anymore. So to + // keep things consistent we advertise ctrl-f which we always handle + // ourselves. Maybe this situation will be cleaned up in SDL 3, but its + // not a huge deal anyway since our Cocoa Mac version behaves cleanly. + return "Ctrl+F"; + } + return {}; +}; + auto AppAdapterSDL::SupportsVSync() -> bool const { return true; } auto AppAdapterSDL::SupportsMaxFPS() -> bool const { return true; } diff --git a/src/ballistica/base/app_adapter/app_adapter_sdl.h b/src/ballistica/base/app_adapter/app_adapter_sdl.h index cc0f0405..78223a24 100644 --- a/src/ballistica/base/app_adapter/app_adapter_sdl.h +++ b/src/ballistica/base/app_adapter/app_adapter_sdl.h @@ -34,7 +34,9 @@ class AppAdapterSDL : public AppAdapter { auto TryRender() -> bool; - auto CanToggleFullscreen() -> bool const override; + auto FullscreenControlAvailable() const -> bool override; + auto FullscreenControlKeyShortcut() const + -> std::optional override; auto SupportsVSync() -> bool const override; auto SupportsMaxFPS() -> bool const override; diff --git a/src/ballistica/base/input/input.cc b/src/ballistica/base/input/input.cc index 06d31f27..c3b27059 100644 --- a/src/ballistica/base/input/input.cc +++ b/src/ballistica/base/input/input.cc @@ -15,6 +15,7 @@ #include "ballistica/base/support/app_config.h" #include "ballistica/base/ui/dev_console.h" #include "ballistica/base/ui/ui.h" +#include "ballistica/shared/buildconfig/buildconfig_common.h" #include "ballistica/shared/foundation/event_loop.h" #include "ballistica/shared/generic/utils.h" @@ -997,10 +998,26 @@ void Input::HandleKeyPress_(const SDL_Keysym& keysym) { } } - // Command-F or Control-F toggles full-screen if the app-adapter supports it. - if (g_base->app_adapter->CanToggleFullscreen()) { - if (!repeat_press && keysym.sym == SDLK_f - && ((keysym.mod & KMOD_CTRL) || (keysym.mod & KMOD_GUI))) { + // Explicitly handle fullscreen-toggles in some cases. + if (g_base->app_adapter->FullscreenControlAvailable()) { + bool do_toggle{}; + // On our Mac SDL builds we support ctrl+F for toggling fullscreen. + // On our nice Cocoa build, fullscreening happens magically through the + // view menu fullscreen controls. + if (g_buildconfig.ostype_macos() && !g_buildconfig.xcode_build()) { + if (!repeat_press && keysym.sym == SDLK_f && ((keysym.mod & KMOD_CTRL))) { + do_toggle = true; + } + } + // On Windows we support both F11 and Alt+Enter for toggling fullscreen. + if (g_buildconfig.ostype_windows()) { + if (!repeat_press + && (keysym.sym == SDLK_F11 + || (keysym.sym == SDLK_RETURN && ((keysym.mod & KMOD_ALT))))) { + do_toggle = true; + } + } + if (do_toggle) { g_base->python->objs() .Get(BasePython::ObjID::kToggleFullscreenCall) .Call(); @@ -1008,12 +1025,15 @@ void Input::HandleKeyPress_(const SDL_Keysym& keysym) { } } - // Control-Q quits. On Mac, the usual Cmd-Q gets handled - // by the app-adapter implicitly. - if (!repeat_press && keysym.sym == SDLK_q && (keysym.mod & KMOD_CTRL)) { - g_base->QuitApp(true); - return; - } + // Control-Q quits. On Mac, the usual Cmd-Q gets handled implicitly by the + // app-adapter. + // UPDATE: Disabling this for now. Looks like standard OS shortcuts like + // Alt+F4 on windows or Cmd-Q on Mac are doing the right thing with SDL + // builds these days so these are not needed. + // if (!repeat_press && keysym.sym == SDLK_q && (keysym.mod & KMOD_CTRL)) { + // g_base->QuitApp(true); + // return; + // } // Let the console intercept stuff if it wants at this point. if (auto* console = g_base->ui->dev_console()) { diff --git a/src/ballistica/base/python/base_python.h b/src/ballistica/base/python/base_python.h index 7b807b1b..fc70c6db 100644 --- a/src/ballistica/base/python/base_python.h +++ b/src/ballistica/base/python/base_python.h @@ -38,6 +38,8 @@ class BasePython { kConfig, kAppOnNativeBootstrappingCompleteCall, kResetToMainMenuCall, + kStoreConfigFullscreenOnCall, + kStoreConfigFullscreenOffCall, kSetConfigFullscreenOnCall, kSetConfigFullscreenOffCall, kNotSignedInScreenMessageCall, diff --git a/src/ballistica/base/python/methods/python_methods_graphics.cc b/src/ballistica/base/python/methods/python_methods_graphics.cc index 99ef62c8..0ebf464b 100644 --- a/src/ballistica/base/python/methods/python_methods_graphics.cc +++ b/src/ballistica/base/python/methods/python_methods_graphics.cc @@ -12,6 +12,7 @@ #include "ballistica/base/python/support/python_context_call_runnable.h" #include "ballistica/core/core.h" #include "ballistica/core/platform/core_platform.h" +#include "ballistica/shared/foundation/macros.h" #include "ballistica/shared/generic/utils.h" #include "ballistica/shared/python/python.h" #include "ballistica/shared/python/python_sys.h" @@ -345,8 +346,7 @@ static PyMethodDef PySafeColorDef = { // ------------------------ get_max_graphics_quality --------------------------- -static auto PyGetMaxGraphicsQuality(PyObject* self, PyObject* args) - -> PyObject* { +static auto PyGetMaxGraphicsQuality(PyObject* self) -> PyObject* { BA_PYTHON_TRY; if (g_base->graphics && g_base->graphics->has_supports_high_quality_graphics_value() @@ -359,9 +359,9 @@ static auto PyGetMaxGraphicsQuality(PyObject* self, PyObject* args) } static PyMethodDef PyGetMaxGraphicsQualityDef = { - "get_max_graphics_quality", // name - PyGetMaxGraphicsQuality, // method - METH_VARARGS, // flags + "get_max_graphics_quality", // name + (PyCFunction)PyGetMaxGraphicsQuality, // method + METH_NOARGS, // flags "get_max_graphics_quality() -> str\n" "\n" @@ -619,24 +619,105 @@ static PyMethodDef PyGetDisplayResolutionDef = { "display. Returns None if resolutions cannot be directly set.", }; -// ------------------------- can_toggle_fullscreen ---------------------------- +// ---------------------- fullscreen_control_available ------------------------- -static auto PyCanToggleFullscreen(PyObject* self) -> PyObject* { +static auto PyFullscreenControlAvailable(PyObject* self) -> PyObject* { BA_PYTHON_TRY; - if (g_base->app_adapter->CanToggleFullscreen()) { + BA_PRECONDITION(g_base->InLogicThread()); + if (g_base->app_adapter->FullscreenControlAvailable()) { Py_RETURN_TRUE; } Py_RETURN_FALSE; BA_PYTHON_CATCH; } -static PyMethodDef PyCanToggleFullscreenDef = { - "can_toggle_fullscreen", // name - (PyCFunction)PyCanToggleFullscreen, // method - METH_NOARGS, // flags +static PyMethodDef PyFullscreenControlAvailableDef = { + "fullscreen_control_available", // name + (PyCFunction)PyFullscreenControlAvailable, // method + METH_NOARGS, // flags - "can_toggle_fullscreen() -> bool\n" + "fullscreen_control_available() -> bool\n" + "\n" + "(internal)\n", +}; + +// --------------------- fullscreen_control_key_shortcut ----------------------- + +static auto PyFullscreenControlKeyShortcut(PyObject* self) -> PyObject* { + BA_PYTHON_TRY; + + BA_PRECONDITION(g_base->InLogicThread()); + BA_PRECONDITION(g_base->app_adapter->FullscreenControlAvailable()); + + auto val = g_base->app_adapter->FullscreenControlKeyShortcut(); + if (val.has_value()) { + return PyUnicode_FromString(val->c_str()); + } + Py_RETURN_NONE; + BA_PYTHON_CATCH; +} + +static PyMethodDef PyFullscreenControlKeyShortcutDef = { + "fullscreen_control_key_shortcut", // name + (PyCFunction)PyFullscreenControlKeyShortcut, // method + METH_NOARGS, // flags + + "fullscreen_control_key_shortcut() -> str | None\n" + "\n" + "(internal)\n", +}; + +// ------------------------ fullscreen_control_get ----------------------------- + +static auto PyFullscreenControlGet(PyObject* self) -> PyObject* { + BA_PYTHON_TRY; + + BA_PRECONDITION(g_base->InLogicThread()); + if (g_base->app_adapter->FullscreenControlGet()) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; + BA_PYTHON_CATCH; +} + +static PyMethodDef PyFullscreenControlGetDef = { + "fullscreen_control_get", // name + (PyCFunction)PyFullscreenControlGet, // method + METH_NOARGS, // flags + + "fullscreen_control_get() -> bool\n" + "\n" + "(internal)\n", +}; + +// ------------------------ fullscreen_control_set ----------------------------- + +static auto PyFullscreenControlSet(PyObject* self, PyObject* args, + PyObject* keywds) -> PyObject* { + BA_PYTHON_TRY; + + BA_PRECONDITION(g_base->InLogicThread()); + + int val{}; + static const char* kwlist[] = {"val", nullptr}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "p", + const_cast(kwlist), &val)) { + return nullptr; + } + + g_base->app_adapter->FullscreenControlSet(val); + + Py_RETURN_NONE; + BA_PYTHON_CATCH; +} + +static PyMethodDef PyFullscreenControlSetDef = { + "fullscreen_control_set", // name + (PyCFunction)PyFullscreenControlSet, // method + METH_VARARGS | METH_KEYWORDS, // flags + + "fullscreen_control_set(val: bool) -> None\n" "\n" "(internal)\n", }; @@ -728,10 +809,13 @@ auto PythonMethodsGraphics::GetMethods() -> std::vector { PyGetMaxGraphicsQualityDef, PySafeColorDef, PyCharStrDef, - PyCanToggleFullscreenDef, + PyFullscreenControlAvailableDef, PySupportsVSyncDef, PySupportsMaxFPSDef, PyShowProgressBarDef, + PyFullscreenControlKeyShortcutDef, + PyFullscreenControlGetDef, + PyFullscreenControlSetDef, }; } diff --git a/src/ballistica/shared/ballistica.cc b/src/ballistica/shared/ballistica.cc index c67f264a..9b9f86f4 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 = 21465; +const int kEngineBuildNumber = 21468; const char* kEngineVersion = "1.7.28"; const int kEngineApiVersion = 8; diff --git a/src/meta/babasemeta/pyembed/binding_base.py b/src/meta/babasemeta/pyembed/binding_base.py index 5e71b25a..848803bd 100644 --- a/src/meta/babasemeta/pyembed/binding_base.py +++ b/src/meta/babasemeta/pyembed/binding_base.py @@ -21,6 +21,8 @@ from babase import ( # The C++ layer looks for this variable: values = [ _hooks.reset_to_main_menu, # kResetToMainMenuCall + _hooks.store_config_fullscreen_on, # kStoreConfigFullscreenOnCall + _hooks.store_config_fullscreen_off, # kStoreConfigFullscreenOffCall _hooks.set_config_fullscreen_on, # kSetConfigFullscreenOnCall _hooks.set_config_fullscreen_off, # kSetConfigFullscreenOffCall _hooks.not_signed_in_screen_message, # kNotSignedInScreenMessageCall