changeset 222:84844649a8fd am

continued: reusable applications
author am@osimis.io
date Tue, 12 Jun 2018 17:21:15 +0200
parents d7b2590744f8
children d30a10d574ec
files .hgignore Applications/BasicApplicationContext.cpp Applications/BasicApplicationContext.h Applications/IBasicApplication.h Applications/Samples/SampleApplicationContext.h Applications/Sdl/BasicSdlApplication.cpp Applications/Sdl/BasicSdlApplicationContext.cpp Applications/Sdl/BasicSdlApplicationContext.h Applications/Sdl/SdlEngine.cpp Applications/Sdl/SdlEngine.h Applications/Wasm/BasicWasmApplication.cpp Applications/Wasm/BasicWasmApplication.h Applications/Wasm/BasicWasmApplicationContext.cpp Applications/Wasm/BasicWasmApplicationContext.h Framework/Viewport/WidgetViewport.cpp Platforms/WebAssembly/Defaults.cpp Platforms/WebAssembly/Defaults.h Platforms/WebAssembly/defaultLibrary.js Platforms/WebAssembly/defaults.js README Resources/CMake/OrthancStoneConfiguration.cmake
diffstat 21 files changed, 417 insertions(+), 125 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Mon Jun 11 14:01:02 2018 +0200
+++ b/.hgignore	Tue Jun 12 17:21:15 2018 +0200
@@ -1,1 +1,2 @@
 Platforms/Generic/CMakeLists.txt.user
+Platforms/Generic/ThirdPartyDownloads/
--- a/Applications/BasicApplicationContext.cpp	Mon Jun 11 14:01:02 2018 +0200
+++ b/Applications/BasicApplicationContext.cpp	Tue Jun 12 17:21:15 2018 +0200
@@ -23,59 +23,4 @@
 
 namespace OrthancStone
 {
-  void BasicApplicationContext::UpdateThread(BasicApplicationContext* that)
-  {
-    while (!that->stopped_)
-    {
-      {
-        ViewportLocker locker(*that);
-        locker.GetViewport().UpdateContent();
-      }
-      
-      boost::this_thread::sleep(boost::posix_time::milliseconds(that->updateDelay_));
-    }
-  }
-  
-
-  BasicApplicationContext::BasicApplicationContext(Orthanc::WebServiceParameters& orthanc) :
-    oracle_(viewportMutex_, 4),  // Use 4 threads to download
-    //oracle_(viewportMutex_, 1),  // Disable threading to be reproducible
-    webService_(oracle_, orthanc),
-    stopped_(true),
-    updateDelay_(100)   // By default, 100ms between each refresh of the content
-  {
-    srand(time(NULL)); 
-  }
-
-
-  IWidget& BasicApplicationContext::SetCentralWidget(IWidget* widget)   // Takes ownership
-  {
-    centralViewport_.SetCentralWidget(widget);
-    return *widget;
-  }
-
-
-  void BasicApplicationContext::Start()
-  {
-    oracle_.Start();
-
-    if (centralViewport_.HasUpdateContent())
-    {
-      stopped_ = false;
-      updateThread_ = boost::thread(UpdateThread, this);
-    }
-  }
-
-
-  void BasicApplicationContext::Stop()
-  {
-    stopped_ = true;
-    
-    if (updateThread_.joinable())
-    {
-      updateThread_.join();
-    }
-    
-    oracle_.Stop();
-  }
 }
--- a/Applications/BasicApplicationContext.h	Mon Jun 11 14:01:02 2018 +0200
+++ b/Applications/BasicApplicationContext.h	Tue Jun 12 17:21:15 2018 +0200
@@ -21,71 +21,22 @@
 
 #pragma once
 
+#include "../Platforms/Generic/OracleWebService.h"
 #include "../Framework/Viewport/WidgetViewport.h"
-#include "../Framework/Volumes/ISlicedVolume.h"
-#include "../Framework/Volumes/IVolumeLoader.h"
-#include "../Framework/Widgets/IWorldSceneInteractor.h"
-#include "../Platforms/Generic/OracleWebService.h"
 
 #include <list>
-#include <boost/thread.hpp>
 
 namespace OrthancStone
 {
   class BasicApplicationContext : public boost::noncopyable
   {
-  private:
-
-    static void UpdateThread(BasicApplicationContext* that);
-
-
-    Oracle              oracle_;
-    OracleWebService    webService_;
-    boost::mutex        viewportMutex_;
-    WidgetViewport      centralViewport_;
-    boost::thread       updateThread_;
-    bool                stopped_;
-    unsigned int        updateDelay_;
 
   public:
-    class ViewportLocker : public boost::noncopyable
-    {
-    private:
-      boost::mutex::scoped_lock  lock_;
-      IViewport&                 viewport_;
+    BasicApplicationContext() {}
 
-    public:
-      ViewportLocker(BasicApplicationContext& that) :
-        lock_(that.viewportMutex_),
-        viewport_(that.centralViewport_)
-      {
-      }
-
-      IViewport& GetViewport() const
-      {
-        return viewport_;
-      }
-    };
-
-    
-    BasicApplicationContext(Orthanc::WebServiceParameters& orthanc);
+    virtual IWebService& GetWebService() = 0;
+    virtual IWidget& SetCentralWidget(IWidget* widget) = 0;   // Takes ownership
 
     virtual ~BasicApplicationContext() {}
-
-    IWidget& SetCentralWidget(IWidget* widget);   // Takes ownership
-
-    IWebService& GetWebService()
-    {
-      return webService_;
-    }
-    
-    void Start();
-
-    void Stop();
-
-    void SetUpdateDelay(unsigned int delay)  // In milliseconds
-    {
-      updateDelay_ = delay;
-    }
   };
 }
--- a/Applications/IBasicApplication.h	Mon Jun 11 14:01:02 2018 +0200
+++ b/Applications/IBasicApplication.h	Tue Jun 12 17:21:15 2018 +0200
@@ -23,6 +23,7 @@
 
 #include "BasicApplicationContext.h"
 #include <boost/program_options.hpp>
+#include "../Framework/Viewport/WidgetViewport.h"
 
 namespace OrthancStone
 {
@@ -63,8 +64,11 @@
     virtual void Initialize(IStatusBar& statusBar,
                             const boost::program_options::variables_map& parameters) = 0;
 
-    virtual BasicApplicationContext& CreateApplicationContext(Orthanc::WebServiceParameters& orthanc) = 0;
-
+#if ORTHANC_ENABLE_SDL == 1
+  virtual BasicApplicationContext& CreateApplicationContext(Orthanc::WebServiceParameters& orthancWebService) = 0;
+#else
+  virtual BasicApplicationContext& CreateApplicationContext(IWebService& orthancWebService, std::shared_ptr<WidgetViewport> centralViewport) = 0;
+#endif
 
     virtual std::string GetTitle() const = 0;
 
--- a/Applications/Samples/SampleApplicationContext.h	Mon Jun 11 14:01:02 2018 +0200
+++ b/Applications/Samples/SampleApplicationContext.h	Tue Jun 12 17:21:15 2018 +0200
@@ -34,7 +34,16 @@
 
 namespace OrthancStone
 {
-  class SampleApplicationContext : public BasicApplicationContext
+
+#if ORTHANC_ENABLE_SDL
+#include "../Sdl/BasicSdlApplicationContext.h"
+typedef BasicSdlApplicationContext BasicApplicationContext_;
+#else
+#include "../Wasm/BasicWasmApplicationContext.h"
+typedef BasicWasmApplicationContext BasicApplicationContext_;
+#endif
+
+  class SampleApplicationContext : public BasicApplicationContext_
   {
   private:
     typedef std::list<ISlicedVolume*>          SlicedVolumes;  // this is actually used by the samples and shall be moved to a SampleApplicationContext
--- a/Applications/Sdl/BasicSdlApplication.cpp	Mon Jun 11 14:01:02 2018 +0200
+++ b/Applications/Sdl/BasicSdlApplication.cpp	Tue Jun 12 17:21:15 2018 +0200
@@ -243,12 +243,12 @@
       LOG(WARNING) << "Creating the widgets of the application";
 
       LogStatusBar statusBar;
-      BasicApplicationContext& context = application.CreateApplicationContext(webService);
+      BasicSdlApplicationContext& context = dynamic_cast<BasicSdlApplicationContext&>(application.CreateApplicationContext(webService));
 
       application.Initialize(statusBar, parameters);
 
       {
-        BasicApplicationContext::ViewportLocker locker(context);
+        BasicSdlApplicationContext::ViewportLocker locker(context);
         locker.GetViewport().SetStatusBar(statusBar);
       }
 
@@ -269,7 +269,7 @@
         SdlEngine sdl(window, context);
 
         {
-          BasicApplicationContext::ViewportLocker locker(context);
+          BasicSdlApplicationContext::ViewportLocker locker(context);
           locker.GetViewport().Register(sdl);  // (*)
         }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Sdl/BasicSdlApplicationContext.cpp	Tue Jun 12 17:21:15 2018 +0200
@@ -0,0 +1,81 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2018 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "BasicSdlApplicationContext.h"
+
+namespace OrthancStone
+{
+  IWidget& BasicSdlApplicationContext::SetCentralWidget(IWidget* widget)   // Takes ownership
+  {
+    centralViewport_.SetCentralWidget(widget);
+    return *widget;
+  }
+
+
+  void BasicSdlApplicationContext::UpdateThread(BasicSdlApplicationContext* that)
+  {
+    while (!that->stopped_)
+    {
+      {
+        ViewportLocker locker(*that);
+        locker.GetViewport().UpdateContent();
+      }
+      
+      boost::this_thread::sleep(boost::posix_time::milliseconds(that->updateDelay_));
+    }
+  }
+  
+
+  BasicSdlApplicationContext::BasicSdlApplicationContext(Orthanc::WebServiceParameters& orthanc) :
+    oracle_(viewportMutex_, 4),  // Use 4 threads to download
+    //oracle_(viewportMutex_, 1),  // Disable threading to be reproducible
+    webService_(oracle_, orthanc),
+    stopped_(true),
+    updateDelay_(100)   // By default, 100ms between each refresh of the content
+  {
+    srand(time(NULL)); 
+  }
+
+
+  void BasicSdlApplicationContext::Start()
+  {
+    oracle_.Start();
+
+    if (centralViewport_.HasUpdateContent())
+    {
+      stopped_ = false;
+      updateThread_ = boost::thread(UpdateThread, this);
+    }
+  }
+
+
+  void BasicSdlApplicationContext::Stop()
+  {
+    stopped_ = true;
+    
+    if (updateThread_.joinable())
+    {
+      updateThread_.join();
+    }
+    
+    oracle_.Stop();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Sdl/BasicSdlApplicationContext.h	Tue Jun 12 17:21:15 2018 +0200
@@ -0,0 +1,91 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2018 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../../Framework/Viewport/WidgetViewport.h"
+#include "../../Framework/Volumes/ISlicedVolume.h"
+#include "../../Framework/Volumes/IVolumeLoader.h"
+#include "../../Framework/Widgets/IWorldSceneInteractor.h"
+#include "../../Platforms/Generic/OracleWebService.h"
+
+#include <list>
+#include <boost/thread.hpp>
+#include "../BasicApplicationContext.h"
+
+namespace OrthancStone
+{
+  class BasicSdlApplicationContext : public BasicApplicationContext
+  {
+  private:
+
+    static void UpdateThread(BasicSdlApplicationContext* that);
+
+    Oracle              oracle_;
+    OracleWebService    webService_;
+    boost::mutex        viewportMutex_;
+    WidgetViewport      centralViewport_;
+    boost::thread       updateThread_;
+    bool                stopped_;
+    unsigned int        updateDelay_;
+
+  public:
+    class ViewportLocker : public boost::noncopyable
+    {
+    private:
+      boost::mutex::scoped_lock  lock_;
+      IViewport&                 viewport_;
+
+    public:
+      ViewportLocker(BasicSdlApplicationContext& that) :
+        lock_(that.viewportMutex_),
+        viewport_(that.centralViewport_)
+      {
+      }
+
+      IViewport& GetViewport() const
+      {
+        return viewport_;
+      }
+    };
+
+    
+    BasicSdlApplicationContext(Orthanc::WebServiceParameters& orthanc);
+
+    virtual ~BasicSdlApplicationContext() {}
+
+    virtual IWidget& SetCentralWidget(IWidget* widget);   // Takes ownership
+
+    virtual IWebService& GetWebService()
+    {
+      return webService_;
+    }
+    
+    void Start();
+
+    void Stop();
+
+    void SetUpdateDelay(unsigned int delay)  // In milliseconds
+    {
+      updateDelay_ = delay;
+    }
+  };
+}
--- a/Applications/Sdl/SdlEngine.cpp	Mon Jun 11 14:01:02 2018 +0200
+++ b/Applications/Sdl/SdlEngine.cpp	Tue Jun 12 17:21:15 2018 +0200
@@ -29,7 +29,7 @@
 
 namespace OrthancStone
 {
-  void SdlEngine::SetSize(BasicApplicationContext::ViewportLocker& locker,
+  void SdlEngine::SetSize(BasicSdlApplicationContext::ViewportLocker& locker,
                           unsigned int width,
                           unsigned int height)
   {
@@ -42,7 +42,7 @@
   {
     if (viewportChanged_)
     {
-      BasicApplicationContext::ViewportLocker locker(context_);
+      BasicSdlApplicationContext::ViewportLocker locker(context_);
       surface_.Render(locker.GetViewport());
 
       viewportChanged_ = false;
@@ -99,7 +99,7 @@
 
 
   SdlEngine::SdlEngine(SdlWindow& window,
-                       BasicApplicationContext& context) :
+                       BasicSdlApplicationContext& context) :
     window_(window),
     context_(context),
     surface_(window),
@@ -119,7 +119,7 @@
     const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
 
     {
-      BasicApplicationContext::ViewportLocker locker(context_);
+      BasicSdlApplicationContext::ViewportLocker locker(context_);
       SetSize(locker, window_.GetWidth(), window_.GetHeight());
       locker.GetViewport().SetDefaultView();
     }
@@ -134,7 +134,7 @@
       while (!stop &&
              SDL_PollEvent(&event))
       {
-        BasicApplicationContext::ViewportLocker locker(context_);
+        BasicSdlApplicationContext::ViewportLocker locker(context_);
 
         if (event.type == SDL_QUIT) 
         {
--- a/Applications/Sdl/SdlEngine.h	Mon Jun 11 14:01:02 2018 +0200
+++ b/Applications/Sdl/SdlEngine.h	Tue Jun 12 17:21:15 2018 +0200
@@ -24,7 +24,7 @@
 #if ORTHANC_ENABLE_SDL == 1
 
 #include "SdlCairoSurface.h"
-#include "../BasicApplicationContext.h"
+#include "BasicSdlApplicationContext.h"
 
 namespace OrthancStone
 {
@@ -32,11 +32,11 @@
   {
   private:
     SdlWindow&                window_;
-    BasicApplicationContext&  context_;
+    BasicSdlApplicationContext&  context_;
     SdlCairoSurface           surface_;
     bool                      viewportChanged_;
 
-    void SetSize(BasicApplicationContext::ViewportLocker& locker,
+    void SetSize(BasicSdlApplicationContext::ViewportLocker& locker,
                  unsigned int width,
                  unsigned int height);
     
@@ -47,7 +47,7 @@
 
   public:
     SdlEngine(SdlWindow& window,
-              BasicApplicationContext& context);
+              BasicSdlApplicationContext& context);
   
     virtual ~SdlEngine();
 
--- a/Applications/Wasm/BasicWasmApplication.cpp	Mon Jun 11 14:01:02 2018 +0200
+++ b/Applications/Wasm/BasicWasmApplication.cpp	Tue Jun 12 17:21:15 2018 +0200
@@ -0,0 +1,43 @@
+#include "BasicWasmApplication.h"
+
+namespace OrthancStone
+{
+
+
+    void BasicWasmApplication::SetStartupParameter(const char* name, const char* value) {
+        startupParameters_.push_back(std::make_tuple(name, value));
+    }
+
+    void BasicWasmApplication::GetStartupParameters(boost::program_options::variables_map& parameters) {
+        boost::program_options::options_description options;
+        DeclareStartupOptions(options);
+        
+        const char* argv[startupParameters_.size() + 1];
+        int argCounter = 0;
+        argv[0] = "Toto.exe";
+        argCounter++;
+
+        std::string cmdLine = "";
+        for (StartupParameters::const_iterator it = startupParameters_.begin(); it != startupParameters_.end(); it++) {
+            char* arg = new char[128];
+            snprintf(arg, 128, "--%s=%s", std::get<0>(*it).c_str(), std::get<1>(*it).c_str());
+            argv[argCounter] = arg;
+            cmdLine = cmdLine + " --" + std::get<0>(*it) + "=" + std::get<1>(*it);
+            argCounter++;
+        }
+
+        printf("simulated cmdLine = %s\n", cmdLine.c_str());
+
+        try
+        {
+            boost::program_options::store(boost::program_options::command_line_parser(argCounter, argv).
+                                            options(options).run(), parameters);
+            boost::program_options::notify(parameters);
+        }
+        catch (boost::program_options::error& e)
+        {
+            printf("Error while parsing the command-line arguments: %s\n", e.what());
+        }
+
+    }
+}
\ No newline at end of file
--- a/Applications/Wasm/BasicWasmApplication.h	Mon Jun 11 14:01:02 2018 +0200
+++ b/Applications/Wasm/BasicWasmApplication.h	Tue Jun 12 17:21:15 2018 +0200
@@ -25,6 +25,7 @@
 #include "../IBasicApplication.h"
 
 #include <boost/program_options.hpp>
+#include <tuple>
 
 #if ORTHANC_ENABLE_SDL == 1
 #error this file shall be included only with the ORTHANC_ENABLE_SDL set to 0
@@ -34,11 +35,19 @@
 {
   class BasicWasmApplication : public IBasicApplication
   {
+    typedef std::list<std::tuple<std::string, std::string>> StartupParameters;
+    StartupParameters startupParameters_;
+
   public:
     virtual ~BasicWasmApplication()
     {
     }
 
+    void SetStartupParameter(const char* name, const char* value);
+    void GetStartupParameters(boost::program_options::variables_map& parameters_);
+    
+    virtual IWidget* GetCentralWidget() = 0;
+
     //static int ExecuteWithWasm(BasicWasmApplication& application);
   };
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Wasm/BasicWasmApplicationContext.cpp	Tue Jun 12 17:21:15 2018 +0200
@@ -0,0 +1,14 @@
+#include "BasicWasmApplicationContext.h"
+
+namespace OrthancStone
+{
+  IWidget& BasicWasmApplicationContext::SetCentralWidget(IWidget* widget)   // Takes ownership
+  {
+    printf("BasicWasmApplicationContext::SetCentralWidget %x %x\n", centralViewport_.get(), widget);
+    assert(centralViewport_.get() != NULL);
+    centralViewport_->SetCentralWidget(widget);
+    printf("BasicWasmApplicationContext::SetCentralWidget done\n");
+    return *widget;
+  }
+
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Wasm/BasicWasmApplicationContext.h	Tue Jun 12 17:21:15 2018 +0200
@@ -0,0 +1,52 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2018 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../BasicApplicationContext.h"
+
+#include <list>
+
+namespace OrthancStone
+{
+  class BasicWasmApplicationContext : public BasicApplicationContext
+  {
+  private:
+    std::shared_ptr<WidgetViewport>  centralViewport_;
+    IWebService& webService_;
+  public:
+    BasicWasmApplicationContext(IWebService& webService, std::shared_ptr<WidgetViewport> centralViewport)  //shared ownership of centralViewport
+    : webService_(webService),
+      centralViewport_(centralViewport)
+    {
+        
+    }
+
+    virtual IWidget& SetCentralWidget(IWidget* widget);   // Takes ownership of central widget
+
+    IWebService& GetWebService()
+    {
+      return webService_;
+    }
+
+    virtual ~BasicWasmApplicationContext() {}
+  };
+}
--- a/Framework/Viewport/WidgetViewport.cpp	Mon Jun 11 14:01:02 2018 +0200
+++ b/Framework/Viewport/WidgetViewport.cpp	Tue Jun 12 17:21:15 2018 +0200
@@ -64,7 +64,7 @@
     }
 
     mouseTracker_.reset(NULL);
-      
+
     centralWidget_.reset(widget);
     centralWidget_->SetViewport(*this);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Platforms/WebAssembly/Defaults.cpp	Tue Jun 12 17:21:15 2018 +0200
@@ -0,0 +1,2 @@
+#include "Defaults.h"
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Platforms/WebAssembly/Defaults.h	Tue Jun 12 17:21:15 2018 +0200
@@ -0,0 +1,62 @@
+#pragma once
+
+#include <Framework/dev.h>
+#include <Framework/Viewport/WidgetViewport.h>
+#include <Framework/Widgets/LayerWidget.h>
+#include <Framework/Widgets/LayoutWidget.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+  
+  extern void ScheduleRedraw();
+  
+#ifdef __cplusplus
+}
+#endif
+
+
+namespace OrthancStone {
+
+  class ChangeObserver :
+    public OrthancStone::IViewport::IObserver
+  {
+  private:
+    // Flag to avoid flooding JavaScript with redundant Redraw requests
+    bool isScheduled_; 
+
+  public:
+    ChangeObserver() :
+      isScheduled_(false)
+    {
+    }
+
+    void Reset()
+    {
+      isScheduled_ = false;
+    }
+
+    virtual void NotifyChange(const OrthancStone::IViewport &scene)
+    {
+      if (!isScheduled_)
+      {
+        ScheduleRedraw();
+        isScheduled_ = true;
+      }
+    }
+  };
+
+
+  class StatusBar : public OrthancStone::IStatusBar
+  {
+  public:
+    virtual void ClearMessage()
+    {
+    }
+
+    virtual void SetMessage(const std::string& message)
+    {
+      printf("%s\n", message.c_str());
+    }
+  };
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Platforms/WebAssembly/defaultLibrary.js	Tue Jun 12 17:21:15 2018 +0200
@@ -0,0 +1,8 @@
+// this file contains the JS method you want to expose to C++ code
+
+mergeInto(LibraryManager.library, {
+    ScheduleRedraw: function() {
+      ScheduleRedraw();
+    }
+  });
+  
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Platforms/WebAssembly/defaults.js	Tue Jun 12 17:21:15 2018 +0200
@@ -0,0 +1,13 @@
+var isPendingRedraw = false;
+
+function ScheduleRedraw()
+{
+  if (!isPendingRedraw) {
+    isPendingRedraw = true;
+    //console.log('Scheduling a refresh of the viewport, as its content changed');
+    window.requestAnimationFrame(function() {
+      isPendingRedraw = false;
+      viewport.Redraw();
+    });
+  }
+}
--- a/README	Mon Jun 11 14:01:02 2018 +0200
+++ b/README	Tue Jun 12 17:21:15 2018 +0200
@@ -72,6 +72,10 @@
 * Optionally, SDL, a cross-platform multimedia library:
   https://www.libsdl.org/
 
+Prerequisites to compile on Ubuntu: 
+```
+sudo apt-get install -y libcairo-dev libpixman-1-dev libsdl2-dev
+```
 
 Installation and usage
 ----------------------
--- a/Resources/CMake/OrthancStoneConfiguration.cmake	Mon Jun 11 14:01:02 2018 +0200
+++ b/Resources/CMake/OrthancStoneConfiguration.cmake	Tue Jun 12 17:21:15 2018 +0200
@@ -137,6 +137,7 @@
 
 set(APPLICATIONS_SOURCES
     ${ORTHANC_STONE_ROOT}/Applications/IBasicApplication.h
+    ${ORTHANC_STONE_ROOT}/Applications/BasicApplicationContext.cpp
     )
 
 if (NOT ORTHANC_SANDBOXED)
@@ -147,8 +148,8 @@
     )
 
   list(APPEND APPLICATIONS_SOURCES
-    ${ORTHANC_STONE_ROOT}/Applications/BasicApplicationContext.cpp
     ${ORTHANC_STONE_ROOT}/Applications/Sdl/BasicSdlApplication.cpp
+    ${ORTHANC_STONE_ROOT}/Applications/Sdl/BasicSdlApplicationContext.cpp
     ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlEngine.cpp
     ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlCairoSurface.cpp
     ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlOrthancSurface.cpp
@@ -157,6 +158,7 @@
 else()
   list(APPEND APPLICATIONS_SOURCES
     ${ORTHANC_STONE_ROOT}/Applications/Wasm/BasicWasmApplication.cpp
+    ${ORTHANC_STONE_ROOT}/Applications/Wasm/BasicWasmApplicationContext.cpp
     )
 endif()
 
@@ -197,6 +199,7 @@
   ${ORTHANC_STONE_ROOT}/Framework/Viewport/CairoContext.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Viewport/CairoFont.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Viewport/CairoSurface.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Viewport/IStatusBar.h
   ${ORTHANC_STONE_ROOT}/Framework/Viewport/WidgetViewport.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Volumes/ImageBuffer3D.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Volumes/SlicedVolumeBase.cpp