diff --git a/core/extension/gdextension_spx_ext.cpp b/core/extension/gdextension_spx_ext.cpp index 475ffdac6db4..3929d0402181 100644 --- a/core/extension/gdextension_spx_ext.cpp +++ b/core/extension/gdextension_spx_ext.cpp @@ -136,6 +136,15 @@ static void gdextension_spx_ext_request_exit(GdInt exit_code) { static void gdextension_spx_ext_on_runtime_panic(GdString msg) { extMgr->on_runtime_panic(msg); } +static void gdextension_spx_ext_pause() { + extMgr->pause(); +} +static void gdextension_spx_ext_resume() { + extMgr->resume(); +} +static void gdextension_spx_ext_is_paused(GdBool* ret_val) { + *ret_val = extMgr->is_paused(); +} static void gdextension_spx_ext_destroy_all_pens() { extMgr->destroy_all_pens(); } @@ -776,6 +785,9 @@ void gdextension_spx_setup_interface() { REGISTER_SPX_INTERFACE_FUNC(spx_camera_get_viewport_rect); REGISTER_SPX_INTERFACE_FUNC(spx_ext_request_exit); REGISTER_SPX_INTERFACE_FUNC(spx_ext_on_runtime_panic); + REGISTER_SPX_INTERFACE_FUNC(spx_ext_pause); + REGISTER_SPX_INTERFACE_FUNC(spx_ext_resume); + REGISTER_SPX_INTERFACE_FUNC(spx_ext_is_paused); REGISTER_SPX_INTERFACE_FUNC(spx_ext_destroy_all_pens); REGISTER_SPX_INTERFACE_FUNC(spx_ext_create_pen); REGISTER_SPX_INTERFACE_FUNC(spx_ext_destroy_pen); diff --git a/core/extension/gdextension_spx_ext.h b/core/extension/gdextension_spx_ext.h index ca8844d5b8fb..1f0a19bb2314 100644 --- a/core/extension/gdextension_spx_ext.h +++ b/core/extension/gdextension_spx_ext.h @@ -86,6 +86,7 @@ typedef void (*GDExtensionSpxCallbackOnEngineStart)(); typedef void (*GDExtensionSpxCallbackOnEngineUpdate)(GdFloat delta); typedef void (*GDExtensionSpxCallbackOnEngineFixedUpdate)(GdFloat delta); typedef void (*GDExtensionSpxCallbackOnEngineDestroy)(); +typedef void (*GDExtensionSpxCallbackOnEnginePause)(GdBool is_paused); typedef void (*GDExtensionSpxCallbackOnSceneSpriteInstantiated)(GdObj obj,GdString type_name); @@ -139,6 +140,7 @@ typedef struct { GDExtensionSpxCallbackOnEngineUpdate func_on_engine_update; GDExtensionSpxCallbackOnEngineFixedUpdate func_on_engine_fixed_update; GDExtensionSpxCallbackOnEngineDestroy func_on_engine_destroy; + GDExtensionSpxCallbackOnEnginePause func_on_engine_pause; // scene GDExtensionSpxCallbackOnSceneSpriteInstantiated func_on_scene_sprite_instantiated; @@ -223,6 +225,9 @@ typedef void (*GDExtensionSpxCameraGetViewportRect)(GdRect2* ret_value); // SpxExt typedef void (*GDExtensionSpxExtRequestExit)(GdInt exit_code); typedef void (*GDExtensionSpxExtOnRuntimePanic)(GdString msg); +typedef void (*GDExtensionSpxExtPause)(); +typedef void (*GDExtensionSpxExtResume)(); +typedef void (*GDExtensionSpxExtIsPaused)(GdBool* ret_value); typedef void (*GDExtensionSpxExtDestroyAllPens)(); typedef void (*GDExtensionSpxExtCreatePen)(GdObj* ret_value); typedef void (*GDExtensionSpxExtDestroyPen)(GdObj obj); diff --git a/core/extension/spx.cpp b/core/extension/spx.cpp index 42c50abe6413..e664ccb42327 100644 --- a/core/extension/spx.cpp +++ b/core/extension/spx.cpp @@ -32,10 +32,18 @@ #include "gdextension_spx_ext.h" #include "scene/main/node.h" #include "scene/main/window.h" +#include "scene/main/scene_tree.h" +#include "core/object/class_db.h" #include "spx_engine.h" #include "spx_input_proxy.h" #include "spx_sprite.h" #include "spx_ui.h" +#include "core/os/thread.h" + +// Simple node class for initialization +class SpxEngineNode : public Node { + GDCLASS(SpxEngineNode, Node); +}; #define SPX_ENGINE SpxEngine::get_singleton() bool Spx::initialed = false; @@ -60,7 +68,7 @@ void Spx::on_start(void *p_tree) { return; } - Node *new_node = memnew(Node); + SpxEngineNode *new_node = memnew(SpxEngineNode); new_node->set_name("SpxEngineNode"); root->add_child(new_node); SPX_ENGINE->set_root_node(tree, new_node); @@ -98,3 +106,25 @@ void Spx::on_destroy() { SPX_ENGINE->on_destroy(); initialed = false; } + +void Spx::pause() { + if (!initialed || !SpxEngine::has_initialed()) { + return; + } + SPX_ENGINE->pause(); +} + +void Spx::resume() { + if (!initialed || !SpxEngine::has_initialed()) { + return; + } + SPX_ENGINE->resume(); +} + +bool Spx::is_paused() { + if (!initialed || !SpxEngine::has_initialed()) { + return false; + } + // Query operations are generally safe from any thread + return SPX_ENGINE->is_paused(); +} diff --git a/core/extension/spx.h b/core/extension/spx.h index 0867b1d37a5f..c2449746a6f5 100644 --- a/core/extension/spx.h +++ b/core/extension/spx.h @@ -41,6 +41,11 @@ class Spx { static void on_fixed_update(double delta); static void on_update(double delta); static void on_destroy(); + + // Pause functionality - public interface + static void pause(); + static void resume(); + static bool is_paused(); }; #endif // SPX_H diff --git a/core/extension/spx_base_mgr.cpp b/core/extension/spx_base_mgr.cpp index a9f2f88651a7..255a512350e0 100644 --- a/core/extension/spx_base_mgr.cpp +++ b/core/extension/spx_base_mgr.cpp @@ -85,3 +85,11 @@ void SpxBaseMgr::on_destroy() { owner = nullptr; } } + +void SpxBaseMgr::on_pause() { + // Default implementation - override in derived classes if needed +} + +void SpxBaseMgr::on_resume() { + // Default implementation - override in derived classes if needed +} diff --git a/core/extension/spx_base_mgr.h b/core/extension/spx_base_mgr.h index 4d3ad9782807..f3cf45f9824a 100644 --- a/core/extension/spx_base_mgr.h +++ b/core/extension/spx_base_mgr.h @@ -74,6 +74,8 @@ class SpxBaseMgr { virtual void on_fixed_update(float delta); virtual void on_destroy(); virtual void on_exit(int exit_code); + virtual void on_pause(); + virtual void on_resume(); virtual ~SpxBaseMgr() = default; // Added virtual destructor to fix -Werror=non-virtual-dtor }; diff --git a/core/extension/spx_engine.cpp b/core/extension/spx_engine.cpp index 1d7bcd28f9f3..c3c0a883c1f5 100644 --- a/core/extension/spx_engine.cpp +++ b/core/extension/spx_engine.cpp @@ -31,8 +31,10 @@ #include "spx_engine.h" #include "core/extension/gdextension.h" #include "core/os/memory.h" +#include "core/os/thread.h" #include "gdextension_spx_ext.h" #include "scene/main/window.h" +#include "scene/main/scene_tree.h" #include "spx_input_mgr.h" #include "spx_audio_mgr.h" #include "spx_physic_mgr.h" @@ -59,6 +61,7 @@ static SpxCallbackInfo get_default_spx_callbacks() { callbacks.func_on_engine_fixed_update = [](GdFloat delta){}; callbacks.func_on_engine_update = [](GdFloat delta){}; callbacks.func_on_engine_destroy = [](){}; + callbacks.func_on_engine_pause = [](GdBool is_paused){}; callbacks.func_on_scene_sprite_instantiated = [](GdObj obj,GdString type_name){}; callbacks.func_on_sprite_ready = [](GdObj obj){}; callbacks.func_on_sprite_updated = [](GdFloat delta){}; @@ -129,6 +132,7 @@ void SpxEngine::register_callbacks(GDExtensionSpxCallbackInfoPtr callback_ptr) { singleton->callbacks = *(SpxCallbackInfo *)callback_ptr; singleton->global_id = 1; + singleton->is_spx_paused = false; } SpxCallbackInfo *SpxEngine::get_callbacks() { @@ -172,7 +176,7 @@ void SpxEngine::on_awake() { } void SpxEngine::on_fixed_update(float delta) { - if (has_exit) { + if (has_exit || is_spx_paused) { return; } for (auto mgr : mgrs) { @@ -184,9 +188,14 @@ void SpxEngine::on_fixed_update(float delta) { } void SpxEngine::on_update(float delta) { - if (has_exit) { + if (has_exit || is_spx_paused) { return; } + if(is_defer_call_pause){ + _on_godot_pause_changed(defer_pause_value); + is_defer_call_pause = false; + } + for (auto mgr : mgrs) { mgr->on_update(delta); } @@ -234,3 +243,63 @@ void SpxEngine::on_destroy() { mgrs.clear(); singleton = nullptr; } + +// SPX Pause functionality implementation with thread safety +void SpxEngine::pause() { + if (tree != nullptr) { + if (Thread::is_main_thread()) { + // Direct call on main thread + tree->set_pause(true); + // Directly notify about pause state change + _on_godot_pause_changed(true); + } else { + // Use SceneTree to defer call to main thread + tree->call_deferred("set_pause", true); + is_defer_call_pause = true; + defer_pause_value = true; + // Defer the pause notification as well + //callable_mp(this, &SpxEngine::_on_godot_pause_changed).call_deferred(true); + } + } +} + +void SpxEngine::resume() { + if (tree != nullptr) { + if (Thread::is_main_thread()) { + // Direct call on main thread + tree->set_pause(false); + // Directly notify about pause state change + _on_godot_pause_changed(false); + } else { + // Use SceneTree to defer call to main thread + tree->call_deferred("set_pause", false); + is_defer_call_pause = true; + defer_pause_value = false; + } + } +} + +bool SpxEngine::is_paused() const { + return is_spx_paused; +} + +// Internal method for Godot pause synchronization +void SpxEngine::_on_godot_pause_changed(bool is_godot_paused) { + if (is_godot_paused != is_spx_paused) { + is_spx_paused = is_godot_paused; + + // Notify all managers about pause/resume + for (auto mgr : mgrs) { + if (is_spx_paused) { + mgr->on_pause(); + } else { + mgr->on_resume(); + } + } + + // Call the pause callback to notify SPX users + if (callbacks.func_on_engine_pause != nullptr) { + callbacks.func_on_engine_pause(is_spx_paused); + } + } +} diff --git a/core/extension/spx_engine.h b/core/extension/spx_engine.h index 9806d8838e40..2123d2ef80d4 100644 --- a/core/extension/spx_engine.h +++ b/core/extension/spx_engine.h @@ -96,6 +96,9 @@ class SpxEngine : SpxBaseMgr { GDExtensionSpxGlobalRuntimePanicCallback on_runtime_panic; GDExtensionSpxGlobalRuntimeExitCallback on_runtime_exit; bool has_exit; + bool is_spx_paused; + bool is_defer_call_pause; + bool defer_pause_value; public: SpxCallbackInfo *get_callbacks() ; GDExtensionSpxGlobalRuntimePanicCallback get_on_runtime_panic() { return on_runtime_panic; } @@ -113,6 +116,14 @@ class SpxEngine : SpxBaseMgr { void on_update(float delta) override; void on_destroy() override; void on_exit(int exit_code) override; + + // SPX Pause functionality - simplified interface + void pause(); + void resume(); + bool is_paused() const; + + // Internal methods for Godot pause synchronization + void _on_godot_pause_changed(bool is_godot_paused); }; #endif // SPX_ENGINE_H diff --git a/core/extension/spx_ext_mgr.cpp b/core/extension/spx_ext_mgr.cpp index c1da844e4dc6..e05480701042 100644 --- a/core/extension/spx_ext_mgr.cpp +++ b/core/extension/spx_ext_mgr.cpp @@ -33,6 +33,7 @@ #include "core/math/color.h" #include "scene/2d/line_2d.h" #include "scene/2d/sprite_2d.h" +#include "spx.h" #include "spx_engine.h" #include "spx_pen.h" #include "spx_res_mgr.h" @@ -177,3 +178,16 @@ void SpxExtMgr::set_pen_stamp_texture(GdObj obj, GdString texture_path) { check_and_get_pen_v() pen->set_stamp_texture(texture_path); } + +// Pause API implementations - delegate to Spx layer +void SpxExtMgr::pause() { + Spx::pause(); +} + +void SpxExtMgr::resume() { + Spx::resume(); +} + +GdBool SpxExtMgr::is_paused() { + return Spx::is_paused(); +} diff --git a/core/extension/spx_ext_mgr.h b/core/extension/spx_ext_mgr.h index a59ed228c202..3271099b0144 100644 --- a/core/extension/spx_ext_mgr.h +++ b/core/extension/spx_ext_mgr.h @@ -59,6 +59,11 @@ class SpxExtMgr : SpxBaseMgr { // engine API void request_exit(GdInt exit_code); void on_runtime_panic(GdString msg); + + // pause API + void pause(); + void resume(); + GdBool is_paused(); // obj APIs void destroy_all_pens(); diff --git a/platform/web/godot_js_spx.cpp b/platform/web/godot_js_spx.cpp index 6897a137c7a3..656edc3d3e43 100644 --- a/platform/web/godot_js_spx.cpp +++ b/platform/web/godot_js_spx.cpp @@ -161,6 +161,18 @@ void gdspx_ext_on_runtime_panic(GdString* msg) { extMgr->on_runtime_panic(*msg); } EMSCRIPTEN_KEEPALIVE +void gdspx_ext_pause() { + extMgr->pause(); +} +EMSCRIPTEN_KEEPALIVE +void gdspx_ext_resume() { + extMgr->resume(); +} +EMSCRIPTEN_KEEPALIVE +void gdspx_ext_is_paused(GdBool* ret_val) { + *ret_val = extMgr->is_paused(); +} +EMSCRIPTEN_KEEPALIVE void gdspx_ext_destroy_all_pens() { extMgr->destroy_all_pens(); } diff --git a/platform/web/godot_js_spx.h b/platform/web/godot_js_spx.h index eaa5533d39ff..ca6126c98d85 100644 --- a/platform/web/godot_js_spx.h +++ b/platform/web/godot_js_spx.h @@ -20,6 +20,7 @@ extern void godot_js_spx_on_engine_start(); extern void godot_js_spx_on_engine_update(GdFloat delta); extern void godot_js_spx_on_engine_fixed_update(GdFloat delta); extern void godot_js_spx_on_engine_destroy(); +extern void godot_js_spx_on_engine_pause(GdBool is_paused); extern void godot_js_spx_on_scene_sprite_instantiated(GdObj* obj,GdString type_name); diff --git a/platform/web/godot_js_spx_callback.cpp b/platform/web/godot_js_spx_callback.cpp index bcba08dc3c35..9c8b47112be4 100644 --- a/platform/web/godot_js_spx_callback.cpp +++ b/platform/web/godot_js_spx_callback.cpp @@ -31,6 +31,9 @@ static void _godot_js_spx_on_engine_fixed_update(GdFloat delta){ static void _godot_js_spx_on_engine_destroy(){ godot_js_spx_on_engine_destroy(); } +static void _godot_js_spx_on_engine_pause(GdBool is_paused){ + godot_js_spx_on_engine_pause(is_paused); +} static void _godot_js_spx_on_scene_sprite_instantiated(GdObj obj,GdString type_name){ godot_js_spx_on_scene_sprite_instantiated(&obj, type_name); @@ -157,6 +160,7 @@ void OS_Web::register_spx_callbacks() { callback_infos->func_on_engine_update = &_godot_js_spx_on_engine_update; callback_infos->func_on_engine_fixed_update = &_godot_js_spx_on_engine_fixed_update; callback_infos->func_on_engine_destroy = &_godot_js_spx_on_engine_destroy; + callback_infos->func_on_engine_pause = &_godot_js_spx_on_engine_pause; callback_infos->func_on_scene_sprite_instantiated = &_godot_js_spx_on_scene_sprite_instantiated; callback_infos->func_on_sprite_ready = &_godot_js_spx_on_sprite_ready; callback_infos->func_on_sprite_updated = &_godot_js_spx_on_sprite_updated; diff --git a/platform/web/js/engine/gdspx.js b/platform/web/js/engine/gdspx.js index 539ad4213b41..e25ea592b9e6 100644 --- a/platform/web/js/engine/gdspx.js +++ b/platform/web/js/engine/gdspx.js @@ -233,6 +233,26 @@ function gdspx_ext_on_runtime_panic(msg) { FreeGdString(_arg0); } +function gdspx_ext_pause() { + var _gdFuncPtr = GodotEngine.rtenv['_gdspx_ext_pause']; + + _gdFuncPtr(); + +} +function gdspx_ext_resume() { + var _gdFuncPtr = GodotEngine.rtenv['_gdspx_ext_resume']; + + _gdFuncPtr(); + +} +function gdspx_ext_is_paused() { + var _gdFuncPtr = GodotEngine.rtenv['_gdspx_ext_is_paused']; + var _retValue = AllocGdBool(); + _gdFuncPtr(_retValue); + var _finalRetValue = ToJsBool(_retValue); + FreeGdBool(_retValue); + return _finalRetValue +} function gdspx_ext_destroy_all_pens() { var _gdFuncPtr = GodotEngine.rtenv['_gdspx_ext_destroy_all_pens']; diff --git a/platform/web/js/libs/library_godot_gdspx.js b/platform/web/js/libs/library_godot_gdspx.js index d63f9f5dca58..d985c2329790 100644 --- a/platform/web/js/libs/library_godot_gdspx.js +++ b/platform/web/js/libs/library_godot_gdspx.js @@ -38,6 +38,12 @@ const GodotGdspx = { window.gdspx_on_engine_destroy(); }, + godot_js_spx_on_engine_pause__proxy: 'sync', + godot_js_spx_on_engine_pause__sig: 'vi', + godot_js_spx_on_engine_pause : function (is_on) { + window.gdspx_on_engine_pause(is_on); + }, + godot_js_spx_on_engine_update__proxy: 'sync', godot_js_spx_on_engine_update__sig: 'vf', @@ -51,12 +57,6 @@ const GodotGdspx = { window.gdspx_on_engine_fixed_update(delta); }, - godot_js_spx_on_engine_destroy__proxy: 'sync', - godot_js_spx_on_engine_destroy__sig: 'v', - godot_js_spx_on_engine_destroy : function () { - window.gdspx_on_engine_destroy(); - }, - godot_js_spx_on_scene_sprite_instantiated__proxy: 'sync', godot_js_spx_on_scene_sprite_instantiated__sig: 'vii', godot_js_spx_on_scene_sprite_instantiated: function (obj, type_name) {