From a0ff58e754f934618de0b3c6198b931235549998 Mon Sep 17 00:00:00 2001 From: slash Date: Sun, 16 Aug 2026 15:52:30 +0530 Subject: [PATCH 1/2] fix(core): use _removeSignal for window focus and blur listeners --- src/core/main.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/main.js b/src/core/main.js index 743cc473b8..31126e659b 100644 --- a/src/core/main.js +++ b/src/core/main.js @@ -126,11 +126,11 @@ class p5 { }; if (typeof window !== 'undefined') { - window.addEventListener('focus', focusHandler); - window.addEventListener('blur', blurHandler); - p5.lifecycleHooks.remove.push(function () { - window.removeEventListener('focus', focusHandler); - window.removeEventListener('blur', blurHandler); + window.addEventListener('focus', focusHandler, { + signal: this._removeSignal + }); + window.addEventListener('blur', blurHandler, { + signal: this._removeSignal }); // Initialization complete, start runtime From 03c0e690d9209dc5fb5dd33b8423e56519294b5e Mon Sep 17 00:00:00 2001 From: slash Date: Sun, 16 Aug 2026 15:53:09 +0530 Subject: [PATCH 2/2] test: add regression test for remove lifecycle hook accumulation --- test/unit/core/environment.js | 72 +++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/test/unit/core/environment.js b/test/unit/core/environment.js index 9438e5c9e3..cd08da0a7e 100644 --- a/test/unit/core/environment.js +++ b/test/unit/core/environment.js @@ -81,6 +81,78 @@ suite('Environment', function () { }); }); + suite('p5.prototype.focused with multiple instances', function () { + test('removing one instance should not remove another instance focus listeners', async function () { + let instance1; + let instance2; + + await Promise.all([ + new Promise(function (resolve) { + new p5(function (p) { + p.setup = function () { + instance1 = p; + resolve(); + }; + }); + }), + new Promise(function (resolve) { + new p5(function (p) { + p.setup = function () { + instance2 = p; + resolve(); + }; + }); + }) + ]); + + window.dispatchEvent(new Event('blur')); + assert.strictEqual(instance2.focused, false); + + await instance1.remove(); + + window.dispatchEvent(new Event('focus')); + assert.strictEqual(instance2.focused, true); + + await instance2.remove(); + }); + }); + + suite('p5.lifecycleHooks.remove cleanup', function () { + test('remove hooks should not accumulate after instances are removed', async function () { + const before = p5.lifecycleHooks.remove.length; + + let instance1; + let instance2; + + await Promise.all([ + new Promise(function (resolve) { + new p5(function (p) { + p.setup = function () { + instance1 = p; + resolve(); + }; + }); + }), + new Promise(function (resolve) { + new p5(function (p) { + p.setup = function () { + instance2 = p; + resolve(); + }; + }); + }) + ]); + + await instance1.remove(); + await instance2.remove(); + + assert.strictEqual( + p5.lifecycleHooks.remove.length, + before + ); + }); + }); + suite('p5.prototype.cursor', function () { test('should change cursor to cross', function () { myp5.cursor(myp5.CROSS);