Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ See docs/process.md for more on how version tagging works.

4.0.20 (in development)
-----------------------
- Added `emscripten_html5_remove_event_listener` function in `html5.h` in order to be
able to remove a single callback. (#25535)
- Embind now requires C++17 or newer. See #24850.

4.0.19 - 11/04/25
Expand Down
46 changes: 46 additions & 0 deletions site/source/docs/api_reference/html5.h.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,52 @@ The ``useCapture`` parameter maps to ``useCapture`` in `EventTarget.addEventLis

Most functions return the result using the type :c:data:`EMSCRIPTEN_RESULT`. Zero and positive values denote success. Negative values signal failure. None of the functions fail or abort by throwing a JavaScript or C++ exception. If a particular browser does not support the given feature, the value :c:data:`EMSCRIPTEN_RESULT_NOT_SUPPORTED` will be returned at the time the callback is registered.

Unregister function
-------------------

In order to unregister a single event handler callback, call the following function:

.. code-block:: cpp

EMSCRIPTEN_RESULT emscripten_html5_remove_event_listener(
const char *target, // ID of the target HTML element.
void *userData, // User-defined data (passed to the callback).
int eventTypeId, // The event type ID (EMSCRIPTEN_EVENT_XXX).
void *callback // Callback function.
);


The ``target``, ``userData`` and ``callback`` parameters are the same parameters provided in ``emscripten_set_some_callback`` with the only difference being that, since this function applies to all types of callbacks, the type of ``callback`` is ``void *``.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe after this paragraph, add something like

Note in particular that the value of `userData` will need to match with the call that was used to register the callback. If you are having trouble, double check the value of `userData`.

to try to reinforce that people will track the userData pointer as being relevant.


Note in particular that the value of ``userData`` will need to match with the call that was used to register the callback. If you are having trouble, double check the value of ``userData``.

The ``eventTypeId`` represents the event type, the same Id received in the callback functions.

.. code-block:: cpp

// Example

bool my_mouse_callback_1(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) {
// ...
}

bool my_mouse_callback_2(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) {
// ...
}

void main() {

// 1. set callbacks for mouse down and mouse move
emscripten_set_mousedown_callback("#mydiv", 0, my_mouse_callback_1);
emscripten_set_mousedown_callback("#mydiv", (void *) 34, my_mouse_callback_2);
emscripten_set_mousemove_callback("#mydiv", 0, my_mouse_callback_1);

// 2. remove these callbacks
emscripten_html5_remove_event_listener("#mydiv", 0, EMSCRIPTEN_EVENT_MOUSEDOWN, my_mouse_callback_1);
emscripten_html5_remove_event_listener("#mydiv", (void *) 34, EMSCRIPTEN_EVENT_MOUSEDOWN, my_mouse_callback_2);
emscripten_html5_remove_event_listener("#mydiv", 0, EMSCRIPTEN_EVENT_MOUSEMOVE, my_mouse_callback_1);
}


Callback functions
------------------
Expand Down
63 changes: 63 additions & 0 deletions src/lib/libhtml5.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,25 @@ var LibraryHTML5 = {
return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}};
},

removeSingleHandler(eventHandler) {
if (!eventHandler.target) {
#if ASSERTIONS
err('removeSingleHandler: the target element for event handler registration does not exist, when processing the following event handler registration:');
console.dir(eventHandler);
#endif
return {{{ cDefs.EMSCRIPTEN_RESULT_UNKNOWN_TARGET }}};
}
for (var i = 0; i < JSEvents.eventHandlers.length; ++i) {
if (JSEvents.eventHandlers[i].target === eventHandler.target
&& JSEvents.eventHandlers[i].eventTypeId === eventHandler.eventTypeId
&& JSEvents.eventHandlers[i].callbackfunc === eventHandler.callbackfunc
&& JSEvents.eventHandlers[i].userData === eventHandler.userData) {
JSEvents._removeHandler(i--);
}
}
return {{{ cDefs.EMSCRIPTEN_RESULT_SUCCESS }}};
},

#if PTHREADS
getTargetThreadForEventCallback(targetThread) {
switch (targetThread) {
Expand Down Expand Up @@ -295,6 +314,8 @@ var LibraryHTML5 = {
var eventHandler = {
target: findEventTarget(target),
eventTypeString,
eventTypeId,
userData,
callbackfunc,
handlerFunc: keyEventHandlerFunc,
useCapture
Expand Down Expand Up @@ -409,6 +430,18 @@ var LibraryHTML5 = {
},
#endif

emscripten_html5_remove_event_listener__proxy: 'sync',
emscripten_html5_remove_event_listener__deps: ['$JSEvents', '$findEventTarget'],
emscripten_html5_remove_event_listener: (target, userData, eventTypeId, callback) => {
var eventHandler = {
target: findEventTarget(target),
userData,
eventTypeId,
callbackfunc: callback,
};
return JSEvents.removeSingleHandler(eventHandler);
},

emscripten_set_keypress_callback_on_thread__proxy: 'sync',
emscripten_set_keypress_callback_on_thread__deps: ['$registerKeyEventCallback'],
emscripten_set_keypress_callback_on_thread: (target, userData, useCapture, callbackfunc, targetThread) =>
Expand Down Expand Up @@ -499,6 +532,8 @@ var LibraryHTML5 = {
allowsDeferredCalls: eventTypeString != 'mousemove' && eventTypeString != 'mouseenter' && eventTypeString != 'mouseleave', // Mouse move events do not allow fullscreen/pointer lock requests to be handled in them!
#endif
eventTypeString,
eventTypeId,
userData,
callbackfunc,
handlerFunc: mouseEventHandlerFunc,
useCapture
Expand Down Expand Up @@ -592,6 +627,8 @@ var LibraryHTML5 = {
allowsDeferredCalls: true,
#endif
eventTypeString,
eventTypeId,
userData,
callbackfunc,
handlerFunc: wheelHandlerFunc,
useCapture
Expand Down Expand Up @@ -664,6 +701,8 @@ var LibraryHTML5 = {
var eventHandler = {
target,
eventTypeString,
eventTypeId,
userData,
callbackfunc,
handlerFunc: uiEventHandlerFunc,
useCapture
Expand Down Expand Up @@ -708,6 +747,8 @@ var LibraryHTML5 = {
var eventHandler = {
target: findEventTarget(target),
eventTypeString,
eventTypeId,
userData,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really love all the repetition here, or the fact that we need to store two extra fields just for this new API, but I guess it makes sense, and the repetitive nature of this file is kind of a pre-existing condition :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. I don't love it either, but you are right, this code is replicated over and over (17 times!). I feel like the API could have been simplified in the first place (having just 1 function call for all the calls using the same callback type and providing the eventTypeId... which would have reduced, for example, 9 mouse related calls into just 1...). It is what is now unfortunately. So that is the only solution I can think of to be able to implement this new API.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me correct what I just said in regards to the count. There are indeed 17 places where I had to change the code, but there are way many more "emscripten_set_xxx_callback". There are 17 types of events supported, so we could not really simplyif this since they all have different callback (function pointer) types.

The example I gave for mouse counts as just 1 on these 17 places because there are 9 "emscripten_set_mousexxx_callback" functions which all funnel into 1 registerMouseEventCallback function call. I feel like this API is way too verbose and having just one emscripten_set_mouse_callback with the eventTypeId as a parameter (which is exactly what the internal registerMouseEventCallback is) would have been plenty in the first place...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed the original API shape is a bit more verbose than what I would like. Pardon, this was my very first API design into Emscripten way back in 2014.

I am not much worried about the repetition though, since it will closure minify to just a condensed ,a in code size for each parameter.

One could create a map of ID->string to avoid carrying both ID and string here, but storing that map would take up more bytes than the added ,a for each function. (and the map would not DCE individual fields out)

callbackfunc,
handlerFunc: focusEventHandlerFunc,
useCapture
Expand Down Expand Up @@ -765,6 +806,8 @@ var LibraryHTML5 = {
var eventHandler = {
target: findEventTarget(target),
eventTypeString,
eventTypeId,
userData,
callbackfunc,
handlerFunc: deviceOrientationEventHandlerFunc,
useCapture
Expand Down Expand Up @@ -835,6 +878,8 @@ var LibraryHTML5 = {
var eventHandler = {
target: findEventTarget(target),
eventTypeString,
eventTypeId,
userData,
callbackfunc,
handlerFunc: deviceMotionEventHandlerFunc,
useCapture
Expand Down Expand Up @@ -917,6 +962,8 @@ var LibraryHTML5 = {
var eventHandler = {
target,
eventTypeString,
eventTypeId,
userData,
callbackfunc,
handlerFunc: orientationChangeEventHandlerFunc,
useCapture
Expand Down Expand Up @@ -1024,6 +1071,8 @@ var LibraryHTML5 = {
var eventHandler = {
target,
eventTypeString,
eventTypeId,
userData,
callbackfunc,
handlerFunc: fullscreenChangeEventhandlerFunc,
useCapture
Expand Down Expand Up @@ -1522,6 +1571,8 @@ var LibraryHTML5 = {
var eventHandler = {
target,
eventTypeString,
eventTypeId,
userData,
callbackfunc,
handlerFunc: pointerlockChangeEventHandlerFunc,
useCapture
Expand Down Expand Up @@ -1566,6 +1617,8 @@ var LibraryHTML5 = {
var eventHandler = {
target,
eventTypeString,
eventTypeId,
userData,
callbackfunc,
handlerFunc: pointerlockErrorEventHandlerFunc,
useCapture
Expand Down Expand Up @@ -1716,6 +1769,8 @@ var LibraryHTML5 = {
var eventHandler = {
target,
eventTypeString,
eventTypeId,
userData,
callbackfunc,
handlerFunc: visibilityChangeEventHandlerFunc,
useCapture
Expand Down Expand Up @@ -1831,6 +1886,8 @@ var LibraryHTML5 = {
allowsDeferredCalls: eventTypeString == 'touchstart' || eventTypeString == 'touchend',
#endif
eventTypeString,
eventTypeId,
userData,
callbackfunc,
handlerFunc: touchEventHandlerFunc,
useCapture
Expand Down Expand Up @@ -1914,6 +1971,8 @@ var LibraryHTML5 = {
allowsDeferredCalls: true,
#endif
eventTypeString,
eventTypeId,
userData,
callbackfunc,
handlerFunc: gamepadEventHandlerFunc,
useCapture
Expand Down Expand Up @@ -2000,6 +2059,8 @@ var LibraryHTML5 = {
var eventHandler = {
target: findEventTarget(target),
eventTypeString,
eventTypeId,
userData,
callbackfunc,
handlerFunc: beforeUnloadEventHandlerFunc,
useCapture
Expand Down Expand Up @@ -2050,6 +2111,8 @@ var LibraryHTML5 = {
var eventHandler = {
target: battery,
eventTypeString,
eventTypeId,
userData,
callbackfunc,
handlerFunc: batteryEventHandlerFunc,
useCapture
Expand Down
2 changes: 2 additions & 0 deletions src/lib/libhtml5_webgl.js
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,8 @@ var LibraryHtml5WebGL = {
var eventHandler = {
target: findEventTarget(target),
eventTypeString,
eventTypeId,
userData,
callbackfunc,
handlerFunc: webGlEventHandlerFunc,
useCapture
Expand Down
1 change: 1 addition & 0 deletions src/lib/libsigs.js
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,7 @@ sigs = {
emscripten_has_threading_support__sig: 'i',
emscripten_hide_mouse__sig: 'v',
emscripten_html5_remove_all_event_listeners__sig: 'v',
emscripten_html5_remove_event_listener__sig: 'ippip',
emscripten_idb_async_clear__sig: 'vpppp',
emscripten_idb_async_delete__sig: 'vppppp',
emscripten_idb_async_exists__sig: 'vppppp',
Expand Down
2 changes: 2 additions & 0 deletions system/include/emscripten/html5.h
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,8 @@ EMSCRIPTEN_RESULT emscripten_get_element_css_size(const char *target __attribute

void emscripten_html5_remove_all_event_listeners(void);

EMSCRIPTEN_RESULT emscripten_html5_remove_event_listener(const char *target __attribute__((nonnull)), void *userData, int eventTypeId, void *callback __attribute__((nonnull)));

#define EM_CALLBACK_THREAD_CONTEXT_MAIN_RUNTIME_THREAD ((pthread_t)0x1)
#define EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD ((pthread_t)0x2)

Expand Down
8 changes: 4 additions & 4 deletions test/codesize/test_codesize_cxx_ctors1.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"a.out.js": 19670,
"a.out.js.gz": 8152,
"a.out.nodebug.wasm": 129456,
"a.out.nodebug.wasm.gz": 49187,
"total": 149126,
"total_gz": 57339,
"a.out.nodebug.wasm": 129503,
"a.out.nodebug.wasm.gz": 49248,
"total": 149173,
"total_gz": 57400,
"sent": [
"__cxa_throw",
"_abort_js",
Expand Down
8 changes: 4 additions & 4 deletions test/codesize/test_codesize_cxx_ctors2.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"a.out.js": 19647,
"a.out.js.gz": 8139,
"a.out.nodebug.wasm": 128883,
"a.out.nodebug.wasm.gz": 48829,
"total": 148530,
"total_gz": 56968,
"a.out.nodebug.wasm": 128930,
"a.out.nodebug.wasm.gz": 48886,
"total": 148577,
"total_gz": 57025,
"sent": [
"__cxa_throw",
"_abort_js",
Expand Down
8 changes: 4 additions & 4 deletions test/codesize/test_codesize_cxx_except.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"a.out.js": 23325,
"a.out.js.gz": 9128,
"a.out.nodebug.wasm": 171264,
"a.out.nodebug.wasm.gz": 57309,
"total": 194589,
"total_gz": 66437,
"a.out.nodebug.wasm": 171311,
"a.out.nodebug.wasm.gz": 57348,
"total": 194636,
"total_gz": 66476,
"sent": [
"__cxa_begin_catch",
"__cxa_end_catch",
Expand Down
8 changes: 4 additions & 4 deletions test/codesize/test_codesize_cxx_except_wasm.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"a.out.js": 19486,
"a.out.js.gz": 8077,
"a.out.nodebug.wasm": 144620,
"a.out.nodebug.wasm.gz": 54869,
"total": 164106,
"total_gz": 62946,
"a.out.nodebug.wasm": 144667,
"a.out.nodebug.wasm.gz": 54907,
"total": 164153,
"total_gz": 62984,
"sent": [
"_abort_js",
"_tzset_js",
Expand Down
8 changes: 4 additions & 4 deletions test/codesize/test_codesize_cxx_except_wasm_legacy.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"a.out.js": 19555,
"a.out.js.gz": 8099,
"a.out.nodebug.wasm": 142209,
"a.out.nodebug.wasm.gz": 54320,
"total": 161764,
"total_gz": 62419,
"a.out.nodebug.wasm": 142256,
"a.out.nodebug.wasm.gz": 54366,
"total": 161811,
"total_gz": 62465,
"sent": [
"_abort_js",
"_tzset_js",
Expand Down
8 changes: 4 additions & 4 deletions test/codesize/test_codesize_cxx_lto.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"a.out.js": 19009,
"a.out.js.gz": 7829,
"a.out.nodebug.wasm": 106443,
"a.out.nodebug.wasm.gz": 42551,
"total": 125452,
"total_gz": 50380,
"a.out.nodebug.wasm": 106439,
"a.out.nodebug.wasm.gz": 42591,
"total": 125448,
"total_gz": 50420,
"sent": [
"a (emscripten_resize_heap)",
"b (_setitimer_js)",
Expand Down
8 changes: 4 additions & 4 deletions test/codesize/test_codesize_cxx_mangle.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"a.out.js": 23375,
"a.out.js.gz": 9148,
"a.out.nodebug.wasm": 235290,
"a.out.nodebug.wasm.gz": 78915,
"total": 258665,
"total_gz": 88063,
"a.out.nodebug.wasm": 235343,
"a.out.nodebug.wasm.gz": 78952,
"total": 258718,
"total_gz": 88100,
"sent": [
"__cxa_begin_catch",
"__cxa_end_catch",
Expand Down
8 changes: 4 additions & 4 deletions test/codesize/test_codesize_cxx_noexcept.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"a.out.js": 19670,
"a.out.js.gz": 8152,
"a.out.nodebug.wasm": 131873,
"a.out.nodebug.wasm.gz": 50187,
"total": 151543,
"total_gz": 58339,
"a.out.nodebug.wasm": 131920,
"a.out.nodebug.wasm.gz": 50242,
"total": 151590,
"total_gz": 58394,
"sent": [
"__cxa_throw",
"_abort_js",
Expand Down
Loading