We had met a bug about _emscripten_set_wheel_callback_on_thread
in web app based on wasm. Relative post: https://www.weiy.city/2025/02/uncaught-typeerror-cannot-read-properties-of-null-reading-onwheel-from-_emscripten_set_wheel_callback_on_thread/
Let’s explore how was the function generated in the final wasm file.
The function _emscripten_set_wheel_callback_on_thread
was from emscripten_set_wheel_callback_on_thread
in src/library_html5.js
. Read js files and html5.h under emsdk to find the called logic.
addToLibrary(LibraryHtml5WebGL); // in src\library_addfunction.js
<---
// emscripten_webgl_init_context_attributes: The method is usually called when a thread starts and exits.
// Example:https://cocalc.com/github/emscripten-core/emscripten/blob/main/test/gl_in_mainthread_after_pthread.c
// Execute in calling thread without proxying needed.
emscripten_webgl_init_context_attributes: (attributes) => { // in src\library_html5_webgl.js
<---
emscripten_webgl_do_create_context__deps: [ // in src\library_html5_webgl.js
$JSEvents:{
//...
emscripten_set_wheel_callback_on_thread: // src\library_html5.js
}
]
addToLibrary: convert these JavaScript functions into WebAssembly, which is a bytecode instruction format similar to assembly language
Code in src\library_addfunction.js:
addToLibrary will convert these js functions to assembly format in the final wasm file. emscripten_set_wheel_callback_on_thread will be added to a thread when it starts and exits by emscripten_webgl_init_context_attributes.
If we changed vtkRenderWindow mannaully, the old window need to be handle by oldSDLWin->Finalize();
to do ReleaseGraphicsResources and make contextId correctly.
//如何正确设置ContextId?那我们就去调用 vtkSDL2OpenGLRenderWindow::Clean
void vtkSDL2OpenGLRenderWindow::Finalize() //vtkSDL2OpenGLRenderWindow 析构函数中被调用
{
this->DestroyWindow();
}
<---
void vtkSDL2OpenGLRenderWindow::DestroyWindow()
{
this->Clean();
if (this->WindowId)
{
SDL_DestroyWindow(this->WindowId);
this->WindowId = nullptr;
}
}
<---
void vtkSDL2OpenGLRenderWindow::Clean()
{
/* finish OpenGL rendering */
if (this->OwnContext && this->ContextId)
{
this->MakeCurrent();
this->CleanUpRenderers();
SDL_GL_DeleteContext(this->ContextId);
}
this->ContextId = nullptr;
}
Then use vtkRenderWindowInteractor::ReInitialize to bind new render window and interactor and set context correctly.
vtkRenderWindowInteractor:
void ReInitialize()
{
this->Initialized = 0;
this->Enabled = 0;
this->Initialize();
}
<---
void vtkOpenGLRenderWindow::Start()
void vtkSDL2OpenGLRenderWindow::Initialize()
<---
SDL_GL_CreateContext