Mercurial > hg > orthanc-stone
diff Framework/Viewport/WebAssemblyViewport.cpp @ 1232:a28861abf888 broker
viewports for WebAssembly
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 09 Dec 2019 17:46:33 +0100 |
parents | 3c9529edf5fd |
children | a4bb8c2dd211 |
line wrap: on
line diff
--- a/Framework/Viewport/WebAssemblyViewport.cpp Mon Dec 09 14:41:37 2019 +0100 +++ b/Framework/Viewport/WebAssemblyViewport.cpp Mon Dec 09 17:46:33 2019 +0100 @@ -21,248 +21,246 @@ #include "WebAssemblyViewport.h" -#include "../StoneException.h" +#include <Core/OrthancException.h> -#include <emscripten/html5.h> +#include <boost/make_shared.hpp> namespace OrthancStone { - WebAssemblyOpenGLViewport::WebAssemblyOpenGLViewport(const std::string& canvas) : - WebAssemblyViewport(canvas), - context_(canvas) + static void ConvertMouseEvent(PointerEvent& target, + const EmscriptenMouseEvent& source, + const ICompositor& compositor) { - compositor_.reset(new OpenGLCompositor(context_, GetScene())); - RegisterContextCallbacks(); - } + int x = static_cast<int>(source.targetX); + int y = static_cast<int>(source.targetY); - WebAssemblyOpenGLViewport::WebAssemblyOpenGLViewport(const std::string& canvas, - boost::shared_ptr<Scene2D>& scene) : - WebAssemblyViewport(canvas, scene), - context_(canvas) - { - compositor_.reset(new OpenGLCompositor(context_, GetScene())); - RegisterContextCallbacks(); - } - - void WebAssemblyOpenGLViewport::UpdateSize() - { - context_.UpdateSize(); // First read the size of the canvas - - if (compositor_.get() != NULL) + switch (source.button) { - compositor_->Refresh(); // Then refresh the content of the canvas - } - } + case 0: + target.SetMouseButton(MouseButton_Left); + break; - /* - typedef EM_BOOL (*em_webgl_context_callback)(int eventType, const void *reserved, void *userData); - - EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED - - EMSCRIPTEN_RESULT emscripten_set_webglcontextlost_callback( - const char *target, void *userData, EM_BOOL useCapture, em_webgl_context_callback callback) + case 1: + target.SetMouseButton(MouseButton_Middle); + break; - EMSCRIPTEN_RESULT emscripten_set_webglcontextrestored_callback( - const char *target, void *userData, EM_BOOL useCapture, em_webgl_context_callback callback) - - */ + case 2: + target.SetMouseButton(MouseButton_Right); + break; - EM_BOOL WebAssemblyOpenGLViewport_OpenGLContextLost_callback( - int eventType, const void* reserved, void* userData) - { - ORTHANC_ASSERT(eventType == EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST); - WebAssemblyOpenGLViewport* viewport = reinterpret_cast<WebAssemblyOpenGLViewport*>(userData); - return viewport->OpenGLContextLost(); + default: + target.SetMouseButton(MouseButton_None); + break; + } + + target.AddPosition(compositor.GetPixelCenterCoordinates(x, y)); + target.SetAltModifier(source.altKey); + target.SetControlModifier(source.ctrlKey); + target.SetShiftModifier(source.shiftKey); } - EM_BOOL WebAssemblyOpenGLViewport_OpenGLContextRestored_callback( - int eventType, const void* reserved, void* userData) - { - ORTHANC_ASSERT(eventType == EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED); - WebAssemblyOpenGLViewport* viewport = reinterpret_cast<WebAssemblyOpenGLViewport*>(userData); - return viewport->OpenGLContextRestored(); - } - void WebAssemblyOpenGLViewport::DisableCompositor() - { - compositor_.reset(); - } - - ICompositor& WebAssemblyOpenGLViewport::GetCompositor() + class WebAssemblyViewport::WasmLock : public ILock { - if (compositor_.get() == NULL) - { - // "HasCompositor()" should have been called - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - else - { - return *compositor_; - } - } + private: + WebAssemblyViewport& that_; - void WebAssemblyOpenGLViewport::Refresh() - { - try + public: + WasmLock(WebAssemblyViewport& that) : + that_(that) { - if (HasCompositor()) - { - GetCompositor().Refresh(); - } - else - { - // this block was added because of (perceived?) bugs in the - // browser where the WebGL contexts are NOT automatically restored - // after being lost. - // the WebGL context has been lost. Sce - - //LOG(ERROR) << "About to call WebAssemblyOpenGLContext::TryRecreate()."; - //LOG(ERROR) << "Before calling it, isContextLost == " << context_.IsContextLost(); + } - if (!context_.IsContextLost()) - { - LOG(TRACE) << "Context restored!"; - //LOG(ERROR) << "After calling it, isContextLost == " << context_.IsContextLost(); - RestoreCompositor(); - UpdateSize(); - } - } + virtual bool HasCompositor() const ORTHANC_OVERRIDE + { + return that_.compositor_.get() != NULL; } - catch (const StoneException& e) + + virtual ICompositor& GetCompositor() ORTHANC_OVERRIDE { - if (e.GetErrorCode() == ErrorCode_WebGLContextLost) + if (that_.compositor_.get() == NULL) { - LOG(WARNING) << "Context is lost! Compositor will be disabled."; - DisableCompositor(); - // we now need to wait for the "context restored" callback + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } else { - throw; + return *that_.compositor_; } } - catch (...) + + virtual ViewportController& GetController() ORTHANC_OVERRIDE + { + assert(that_.controller_); + return *that_.controller_; + } + + virtual void Invalidate() ORTHANC_OVERRIDE { - // something else nasty happened - throw; + that_.Invalidate(); } + }; + + + EM_BOOL WebAssemblyViewport::OnRequestAnimationFrame(double time, void *userData) + { + WebAssemblyViewport& that = *reinterpret_cast<WebAssemblyViewport*>(userData); + + if (that.compositor_.get() != NULL && + that.controller_ /* should always be true */) + { + that.Paint(*that.compositor_, *that.controller_); + } + + return true; } - void WebAssemblyOpenGLViewport::RestoreCompositor() + + EM_BOOL WebAssemblyViewport::OnResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData) { - // the context must have been restored! - ORTHANC_ASSERT(!context_.IsContextLost()); - if (compositor_.get() == NULL) + WebAssemblyViewport& that = *reinterpret_cast<WebAssemblyViewport*>(userData); + + if (that.compositor_.get() != NULL) { - compositor_.reset(new OpenGLCompositor(context_, GetScene())); + that.UpdateSize(*that.compositor_); + that.Invalidate(); } - else - { - LOG(WARNING) << "RestoreCompositor() called for \"" << GetCanvasIdentifier() << "\" while it was NOT lost! Nothing done."; - } + + return true; } - bool WebAssemblyOpenGLViewport::OpenGLContextLost() + + EM_BOOL WebAssemblyViewport::OnMouseDown(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) { - LOG(ERROR) << "WebAssemblyOpenGLViewport::OpenGLContextLost() for canvas: " << GetCanvasIdentifier(); - DisableCompositor(); + WebAssemblyViewport& that = *reinterpret_cast<WebAssemblyViewport*>(userData); + + LOG(INFO) << "mouse down: " << that.GetFullCanvasId(); + + if (that.compositor_.get() != NULL && + that.interactor_.get() != NULL) + { + PointerEvent pointer; + ConvertMouseEvent(pointer, *mouseEvent, *that.compositor_); + + that.controller_->HandleMousePress(*that.interactor_, pointer, + that.compositor_->GetCanvasWidth(), + that.compositor_->GetCanvasHeight()); + that.Invalidate(); + } + + return true; + } + + + EM_BOOL WebAssemblyViewport::OnMouseMove(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) + { + WebAssemblyViewport& that = *reinterpret_cast<WebAssemblyViewport*>(userData); + + if (that.compositor_.get() != NULL && + that.controller_->HasActiveTracker()) + { + PointerEvent pointer; + ConvertMouseEvent(pointer, *mouseEvent, *that.compositor_); + that.controller_->HandleMouseMove(pointer); + that.Invalidate(); + } + return true; } - bool WebAssemblyOpenGLViewport::OpenGLContextRestored() + + EM_BOOL WebAssemblyViewport::OnMouseUp(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) { - LOG(ERROR) << "WebAssemblyOpenGLViewport::OpenGLContextRestored() for canvas: " << GetCanvasIdentifier(); - - // maybe the context has already been restored by other means (the - // Refresh() function) - if (!HasCompositor()) + WebAssemblyViewport& that = *reinterpret_cast<WebAssemblyViewport*>(userData); + + if (that.compositor_.get() != NULL) { - RestoreCompositor(); - UpdateSize(); + PointerEvent pointer; + ConvertMouseEvent(pointer, *mouseEvent, *that.compositor_); + that.controller_->HandleMouseRelease(pointer); + that.Invalidate(); } - return false; + + return true; } - void WebAssemblyOpenGLViewport::RegisterContextCallbacks() + + void WebAssemblyViewport::Invalidate() { -#if 0 - // DISABLED ON 2019-08-20 and replaced by external JS calls because I could - // not get emscripten API to work - // TODO: what's the impact of userCapture=true ? - const char* canvasId = GetCanvasIdentifier().c_str(); - void* that = reinterpret_cast<void*>(this); - EMSCRIPTEN_RESULT status = EMSCRIPTEN_RESULT_SUCCESS; + emscripten_request_animation_frame(OnRequestAnimationFrame, this); + } + - //status = emscripten_set_webglcontextlost_callback(canvasId, that, true, WebAssemblyOpenGLViewport_OpenGLContextLost_callback); - //if (status != EMSCRIPTEN_RESULT_SUCCESS) - //{ - // std::stringstream ss; - // ss << "Error while calling emscripten_set_webglcontextlost_callback for: \"" << GetCanvasIdentifier() << "\""; - // std::string msg = ss.str(); - // LOG(ERROR) << msg; - // ORTHANC_ASSERT(false, msg.c_str()); - //} - - status = emscripten_set_webglcontextrestored_callback(canvasId, that, true, WebAssemblyOpenGLViewport_OpenGLContextRestored_callback); - if (status != EMSCRIPTEN_RESULT_SUCCESS) + void WebAssemblyViewport::AcquireCompositor(ICompositor* compositor /* takes ownership */) + { + if (compositor == NULL) { - std::stringstream ss; - ss << "Error while calling emscripten_set_webglcontextrestored_callback for: \"" << GetCanvasIdentifier() << "\""; - std::string msg = ss.str(); - LOG(ERROR) << msg; - ORTHANC_ASSERT(false, msg.c_str()); + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); } - LOG(TRACE) << "WebAssemblyOpenGLViewport::RegisterContextCallbacks() SUCCESS!!!"; -#endif + else + { + compositor_.reset(compositor); + } } - WebAssemblyCairoViewport::WebAssemblyCairoViewport(const std::string& canvas) : - WebAssemblyViewport(canvas), - canvas_(canvas), - compositor_(GetScene(), 1024, 768) + + WebAssemblyViewport::WebAssemblyViewport(const std::string& canvasId, + const Scene2D* scene) : + shortCanvasId_(canvasId), + fullCanvasId_("#" + canvasId), + interactor_(new DefaultViewportInteractor) { - } + if (scene == NULL) + { + controller_ = boost::make_shared<ViewportController>(); + } + else + { + controller_ = boost::make_shared<ViewportController>(*scene); + } + + LOG(INFO) << "Initializing Stone viewport on HTML canvas: " << canvasId; - WebAssemblyCairoViewport::WebAssemblyCairoViewport(const std::string& canvas, - boost::shared_ptr<Scene2D>& scene) : - WebAssemblyViewport(canvas, scene), - canvas_(canvas), - compositor_(GetScene(), 1024, 768) - { + if (canvasId.empty() || + canvasId[0] == '#') + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, + "The canvas identifier must not start with '#'"); + } + + // Disable right-click on the canvas (i.e. context menu) + EM_ASM({ + document.getElementById(UTF8ToString($0)).oncontextmenu = function(event) { + event.preventDefault(); + } + }, + canvasId.c_str() // $0 + ); + + // It is not possible to monitor the resizing of individual + // canvas, so we track the full window of the browser + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, this, false, OnResize); + + emscripten_set_mousedown_callback(fullCanvasId_.c_str(), this, false, OnMouseDown); + emscripten_set_mousemove_callback(fullCanvasId_.c_str(), this, false, OnMouseMove); + emscripten_set_mouseup_callback(fullCanvasId_.c_str(), this, false, OnMouseUp); } - void WebAssemblyCairoViewport::UpdateSize() + + IViewport::ILock* WebAssemblyViewport::Lock() { - LOG(INFO) << "updating cairo viewport size"; - double w, h; - emscripten_get_element_css_size(canvas_.c_str(), &w, &h); - - /** - * Emscripten has the function emscripten_get_element_css_size() - * to query the width and height of a named HTML element. I'm - * calling this first to get the initial size of the canvas DOM - * element, and then call emscripten_set_canvas_size() to - * initialize the framebuffer size of the canvas to the same - * size as its DOM element. - * https://floooh.github.io/2017/02/22/emsc-html.html - **/ - unsigned int canvasWidth = 0; - unsigned int canvasHeight = 0; - - if (w > 0 || - h > 0) - { - canvasWidth = static_cast<unsigned int>(boost::math::iround(w)); - canvasHeight = static_cast<unsigned int>(boost::math::iround(h)); - } - - emscripten_set_canvas_element_size(canvas_.c_str(), canvasWidth, canvasHeight); - compositor_.UpdateSize(canvasWidth, canvasHeight); + return new WasmLock(*this); } - void WebAssemblyCairoViewport::Refresh() + + void WebAssemblyViewport::AcquireInteractor(IViewportInteractor* interactor) { - LOG(INFO) << "refreshing cairo viewport, TODO: blit to the canvans.getContext('2d')"; - GetCompositor().Refresh(); + if (interactor == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + else + { + interactor_.reset(interactor); + } } }