@@ -342,14 +342,61 @@ namespace App::Modules {
342342 return render_obj;
343343 }
344344
345+ class JSLifecycleListener : public Hooray ::LifecycleListener {
346+ public:
347+ JSLifecycleListener (qjspp::Engine& engine, JSApplication& app, qjspp::Value user_app): app(app), engine(engine), user_app_(std::move(user_app)) {
348+
349+ cached_update_obj_ = create_update_context_object (engine);
350+ cached_render_obj_ = create_draw_render_object (engine, app.rengine .get_buffer ());
351+
352+ if (auto fn = std::make_unique<qjspp::Value>(user_app_.get (" onInit" )); fn->is_function ()) {
353+ on_init_fn_ = std::move (fn);
354+ }
355+
356+ if (auto fn = std::make_unique<qjspp::Value>(user_app_.get (" onUpdate" )); fn->is_function ()) {
357+ on_update_fn_ = std::move (fn);
358+ }
359+
360+ if (auto fn = std::make_unique<qjspp::Value>(user_app_.get (" onDraw" )); fn->is_function ()) {
361+ on_draw_fn_ = std::move (fn);
362+ }
363+ }
364+
365+ bool on_init () override {
366+ if (!on_init_fn_) return false ;
367+ std::ignore = on_init_fn_->call_method (user_app_, {});
368+ return false ;
369+ }
370+
371+ bool on_update (float delta_time) override {
372+ if (!on_update_fn_) return false ;
373+ std::ignore = on_update_fn_->call_method (user_app_, { cached_update_obj_.clone () });
374+ return false ;
375+ }
376+
377+ bool on_draw () override {
378+ if (!on_draw_fn_) return false ;
379+ std::ignore = on_draw_fn_->call_method (user_app_, { cached_render_obj_.clone () });
380+ return false ;
381+ }
382+
383+ private:
384+ JSApplication& app;
385+ qjspp::Value user_app_;
386+ qjspp::Engine& engine;
387+ qjspp::Value cached_update_obj_;
388+ qjspp::Value cached_render_obj_;
389+ std::unique_ptr<qjspp::Value> on_init_fn_;
390+ std::unique_ptr<qjspp::Value> on_update_fn_;
391+ std::unique_ptr<qjspp::Value> on_draw_fn_;
392+ };
393+
345394 class JSInputListener : public Hooray ::InputListener {
346395 public:
347- JSInputListener (qjspp::Engine& engine, qjspp::Value user_app) : engine(engine), user_app_(std::move(user_app)) {
348- // Cache JavaScript callback functions if they exist on the JS object
396+ JSInputListener (qjspp::Engine& engine, JSApplication& app, qjspp::Value user_app) : app(app), engine(engine), user_app_(std::move(user_app)) {
349397 if (auto fn = std::make_unique<qjspp::Value>(user_app_.get (" onKeyPressed" )); fn->is_function ()) {
350398 on_key_pressed_fn_ = std::move (fn);
351399 }
352-
353400 if (auto fn = std::make_unique<qjspp::Value>(user_app_.get (" onMousePressed" )); fn->is_function ()) {
354401 on_mouse_pressed_fn_ = std::move (fn);
355402 }
@@ -372,6 +419,7 @@ namespace App::Modules {
372419 }
373420
374421 private:
422+ JSApplication& app;
375423 qjspp::Value user_app_;
376424 qjspp::Engine& engine;
377425 std::unique_ptr<qjspp::Value> on_key_pressed_fn_;
@@ -391,41 +439,25 @@ namespace App::Modules {
391439 });
392440
393441 cls.instance_method (" run" , [&engine](JSApplication* app, const qjspp::ArgList& args) -> qjspp::Value {
394- if (!app) return {};
442+ if (!app || args. empty () ) return {};
395443
396444 const qjspp::Value user_app = args[0 ].clone ();
397445
398- const auto js_listener = std::make_shared<JSInputListener>(engine, user_app.clone ());
399- app->rengine .get_input_manager ().add_listener (js_listener);
400-
401- if (const qjspp::Value on_init_func = user_app.get (" onInit" ); on_init_func.is_function ()) {
402- auto func_ptr = std::make_shared<qjspp::Value>(on_init_func.clone ());
403- auto app_ptr = std::make_shared<qjspp::Value>(user_app.clone ());
404- app->rengine .set_on_init ([func_ptr, app_ptr]() {
405- std::ignore = func_ptr->call_method (*app_ptr, {});
406- });
407- }
446+ // Instantiate listeners once for this application run session
447+ auto input_listener = std::make_shared<JSInputListener>(engine, *app, user_app.clone ());
448+ auto lifecycle_listener = std::make_shared<JSLifecycleListener>(engine, *app, user_app.clone ());
408449
409- if (const qjspp::Value on_update_func = user_app.get (" onUpdate" ); on_update_func.is_function ()) {
410- auto func_ptr = std::make_shared<qjspp::Value>(on_update_func.clone ());
411- auto app_ptr = std::make_shared<qjspp::Value>(user_app.clone ());
412- auto update_obj_ptr = std::make_shared<qjspp::Value>(create_update_context_object (engine));
413- app->rengine .set_on_update ([func_ptr, app_ptr, update_obj_ptr](float delta) {
414- std::ignore = func_ptr->call_method (*app_ptr, { update_obj_ptr->clone () });
415- });
416- }
417-
418- if (const qjspp::Value on_draw_func = user_app.get (" onDraw" ); on_draw_func.is_function ()) {
419- auto func_ptr = std::make_shared<qjspp::Value>(on_draw_func.clone ());
420- auto app_ptr = std::make_shared<qjspp::Value>(user_app.clone ());
421- auto render_obj_ptr = std::make_shared<qjspp::Value>(create_draw_render_object (engine, app->rengine .get_buffer ()));
422- app->rengine .set_on_draw ([func_ptr, app_ptr, render_obj_ptr]() {
423- std::ignore = func_ptr->call_method (*app_ptr, { render_obj_ptr->clone () });
424- });
425- }
450+ // Register listeners with the engine managers
451+ app->rengine .get_input_manager ().add_listener (input_listener);
452+ app->rengine .get_lifecycle_manager ().add_listener (lifecycle_listener);
426453
454+ // Run the main game/render loop
427455 app->rengine .run ();
428456
457+ // Cleanup listeners upon exiting the main loop to prevent leaks or dangling references
458+ app->rengine .get_input_manager ().remove_listener (input_listener);
459+ app->rengine .get_lifecycle_manager ().remove_listener (lifecycle_listener);
460+
429461 return {};
430462 });
431463
0 commit comments