changeset 221:d7b2590744f8 am

wip: building applications reusable in SDL and WASM
author am@osimis.io
date Mon, 11 Jun 2018 14:01:02 +0200
parents 26e3bfe30e66
children 84844649a8fd
files Applications/BasicApplication.cpp Applications/BasicApplication.h Applications/BasicApplicationContext.cpp Applications/BasicApplicationContext.h Applications/IBasicApplication.cpp Applications/IBasicApplication.h Applications/Samples/SampleApplicationBase.h Applications/Samples/SampleApplicationContext.cpp Applications/Samples/SampleApplicationContext.h Applications/Sdl/BasicSdlApplication.cpp Applications/Sdl/BasicSdlApplication.h Applications/Wasm/BasicWasmApplication.cpp Applications/Wasm/BasicWasmApplication.h Platforms/Generic/CMakeLists.txt Platforms/WebAssembly/WasmWebService.cpp Platforms/WebAssembly/WasmWebService.h Platforms/WebAssembly/WasmWebService.js Resources/CMake/OrthancStoneConfiguration.cmake
diffstat 17 files changed, 1216 insertions(+), 360 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/BasicApplication.cpp	Mon Jun 11 14:01:02 2018 +0200
@@ -0,0 +1,291 @@
+/**
+ * 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 "IBasicApplication.h"
+
+#include "../Framework/Toolbox/MessagingToolbox.h"
+#include "Sdl/SdlEngine.h"
+
+#include <Core/Logging.h>
+#include <Core/HttpClient.h>
+#include <Plugins/Samples/Common/OrthancHttpConnection.h>
+
+namespace OrthancStone
+{
+  // Anonymous namespace to avoid clashes against other compilation modules
+  namespace
+  {
+    class LogStatusBar : public IStatusBar
+    {
+    public:
+      virtual void ClearMessage()
+      {
+      }
+
+      virtual void SetMessage(const std::string& message)
+      {
+        LOG(WARNING) << message;
+      }
+    };
+  }
+
+
+#if ORTHANC_ENABLE_SDL == 1
+  static void DeclareSdlCommandLineOptions(boost::program_options::options_description& options)
+  {
+    // Declare the supported parameters
+    boost::program_options::options_description generic("Generic options");
+    generic.add_options()
+      ("help", "Display this help and exit")
+      ("verbose", "Be verbose in logs")
+      ("orthanc", boost::program_options::value<std::string>()->default_value("http://localhost:8042/"),
+       "URL to the Orthanc server")
+      ("username", "Username for the Orthanc server")
+      ("password", "Password for the Orthanc server")
+      ("https-verify", boost::program_options::value<bool>()->default_value(true), "Check HTTPS certificates")
+      ;
+
+    options.add(generic);
+
+    boost::program_options::options_description sdl("SDL options");
+    sdl.add_options()
+      ("width", boost::program_options::value<int>()->default_value(1024), "Initial width of the SDL window")
+      ("height", boost::program_options::value<int>()->default_value(768), "Initial height of the SDL window")
+      ("opengl", boost::program_options::value<bool>()->default_value(true), "Enable OpenGL in SDL")
+      ;
+
+    options.add(sdl);
+  }
+
+
+  int IBasicApplication::ExecuteWithSdl(IBasicApplication& application,
+                                        int argc, 
+                                        char* argv[])
+  {
+    /******************************************************************
+     * Initialize all the subcomponents of Orthanc Stone
+     ******************************************************************/
+
+    Orthanc::Logging::Initialize();
+    Orthanc::HttpClient::InitializeOpenSsl();
+    Orthanc::HttpClient::GlobalInitialize();
+    SdlWindow::GlobalInitialize();
+
+
+    /******************************************************************
+     * Declare and parse the command-line options of the application
+     ******************************************************************/
+
+    boost::program_options::options_description options;
+    DeclareSdlCommandLineOptions(options);   
+    application.DeclareCommandLineOptions(options);
+
+    boost::program_options::variables_map parameters;
+    bool error = false;
+
+    try
+    {
+      boost::program_options::store(boost::program_options::command_line_parser(argc, argv).
+                                    options(options).run(), parameters);
+      boost::program_options::notify(parameters);    
+    }
+    catch (boost::program_options::error& e)
+    {
+      LOG(ERROR) << "Error while parsing the command-line arguments: " << e.what();
+      error = true;
+    }
+
+
+    /******************************************************************
+     * Configure the application with the command-line parameters
+     ******************************************************************/
+
+    if (error || parameters.count("help")) 
+    {
+      std::cout << std::endl
+                << "Usage: " << argv[0] << " [OPTION]..."
+                << std::endl
+                << "Orthanc, lightweight, RESTful DICOM server for healthcare and medical research."
+                << std::endl << std::endl
+                << "Demonstration application of Orthanc Stone using SDL."
+                << std::endl;
+
+      std::cout << options << "\n";
+      return error ? -1 : 0;
+    }
+
+    if (parameters.count("https-verify") &&
+        !parameters["https-verify"].as<bool>())
+    {
+      LOG(WARNING) << "Turning off verification of HTTPS certificates (unsafe)";
+      Orthanc::HttpClient::ConfigureSsl(false, "");
+    }
+
+    if (parameters.count("verbose"))
+    {
+      Orthanc::Logging::EnableInfoLevel(true);
+    }
+
+    if (!parameters.count("width") ||
+        !parameters.count("height") ||
+        !parameters.count("opengl"))
+    {
+      LOG(ERROR) << "Parameter \"width\", \"height\" or \"opengl\" is missing";
+      return -1;
+    }
+
+    int w = parameters["width"].as<int>();
+    int h = parameters["height"].as<int>();
+    if (w <= 0 || h <= 0)
+    {
+      LOG(ERROR) << "Parameters \"width\" and \"height\" must be positive";
+      return -1;
+    }
+
+    unsigned int width = static_cast<unsigned int>(w);
+    unsigned int height = static_cast<unsigned int>(h);
+    LOG(WARNING) << "Initial display size: " << width << "x" << height;
+
+    bool opengl = parameters["opengl"].as<bool>();
+    if (opengl)
+    {
+      LOG(WARNING) << "OpenGL is enabled, disable it with option \"--opengl=off\" if the application crashes";
+    }
+    else
+    {
+      LOG(WARNING) << "OpenGL is disabled, enable it with option \"--opengl=on\" for best performance";
+    }
+
+    bool success = true;
+    try
+    {
+      /****************************************************************
+       * Initialize the connection to the Orthanc server
+       ****************************************************************/
+
+      Orthanc::WebServiceParameters webService;
+
+      if (parameters.count("orthanc"))
+      {
+        webService.SetUrl(parameters["orthanc"].as<std::string>());
+      }
+
+      if (parameters.count("username"))
+      {
+        webService.SetUsername(parameters["username"].as<std::string>());
+      }
+
+      if (parameters.count("password"))
+      {
+        webService.SetPassword(parameters["password"].as<std::string>());
+      }
+
+      LOG(WARNING) << "URL to the Orthanc REST API: " << webService.GetUrl();
+
+      {
+        OrthancPlugins::OrthancHttpConnection orthanc(webService);
+        if (!MessagingToolbox::CheckOrthancVersion(orthanc))
+        {
+          LOG(ERROR) << "Your version of Orthanc is incompatible with Stone of Orthanc, please upgrade";
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
+        }
+      }
+
+
+      /****************************************************************
+       * Initialize the application
+       ****************************************************************/
+
+      LOG(WARNING) << "Creating the widgets of the application";
+
+      LogStatusBar statusBar;
+      BasicApplicationContext context(webService);
+
+      application.Initialize(context, statusBar, parameters);
+
+      {
+        BasicApplicationContext::ViewportLocker locker(context);
+        locker.GetViewport().SetStatusBar(statusBar);
+      }
+
+      std::string title = application.GetTitle();
+      if (title.empty())
+      {
+        title = "Stone of Orthanc";
+      }
+
+      {
+        /**************************************************************
+         * Run the application inside a SDL window
+         **************************************************************/
+
+        LOG(WARNING) << "Starting the application";
+
+        SdlWindow window(title.c_str(), width, height, opengl);
+        SdlEngine sdl(window, context);
+
+        {
+          BasicApplicationContext::ViewportLocker locker(context);
+          locker.GetViewport().Register(sdl);  // (*)
+        }
+
+        context.Start();
+        sdl.Run();
+
+        LOG(WARNING) << "Stopping the application";
+
+        // Don't move the "Stop()" command below out of the block,
+        // otherwise the application might crash, because the
+        // "SdlEngine" is an observer of the viewport (*) and the
+        // update thread started by "context.Start()" would call a
+        // destructed object (the "SdlEngine" is deleted with the
+        // lexical scope).
+        context.Stop();
+      }
+
+
+      /****************************************************************
+       * Finalize the application
+       ****************************************************************/
+
+      LOG(WARNING) << "The application has stopped";
+      application.Finalize();
+    }
+    catch (Orthanc::OrthancException& e)
+    {
+      LOG(ERROR) << "EXCEPTION: " << e.What();
+      success = false;
+    }
+
+
+    /******************************************************************
+     * Finalize all the subcomponents of Orthanc Stone
+     ******************************************************************/
+
+    SdlWindow::GlobalFinalize();
+    Orthanc::HttpClient::GlobalFinalize();
+    Orthanc::HttpClient::FinalizeOpenSsl();
+
+    return (success ? 0 : -1);
+  }
+#endif
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/BasicApplication.h	Mon Jun 11 14:01:02 2018 +0200
@@ -0,0 +1,68 @@
+/**
+ * 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 <boost/program_options.hpp>
+
+#if ORTHANC_ENABLE_SDL == 1
+#  include <SDL.h>   // Necessary to avoid undefined reference to `SDL_main'
+#endif
+
+namespace OrthancStone
+{
+  class IBasicApplication : public boost::noncopyable
+  {
+  public:
+    virtual ~IBasicApplication()
+    {
+    }
+
+    virtual void DeclareStartupOption(const std::string& name, const std::string& defaultValue, const std::string& helpText) = 0;
+    virtual void Initialize(IStatusBar& statusBar, const std::map<std::string, std::string> startupOptions);
+
+
+    virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) = 0;
+
+    virtual std::string GetTitle() const = 0;
+
+    virtual void Initialize(BasicApplicationContext& context,
+                            IStatusBar& statusBar,
+                            const boost::program_options::variables_map& parameters) = 0;
+
+    virtual void Finalize() = 0;
+
+#if ORTHANC_ENABLE_SDL == 1
+    static int ExecuteWithSdl(IBasicApplication& application,
+                              int argc, 
+                              char* argv[]);
+#endif
+  };
+
+  class IBasicSdlApplication : public IBasicApplication
+  {
+    public:
+
+    
+  }
+}
--- a/Applications/BasicApplicationContext.cpp	Fri Jun 08 14:41:45 2018 +0200
+++ b/Applications/BasicApplicationContext.cpp	Mon Jun 11 14:01:02 2018 +0200
@@ -48,81 +48,18 @@
   }
 
 
-  BasicApplicationContext::~BasicApplicationContext()
-  {
-    for (Interactors::iterator it = interactors_.begin(); it != interactors_.end(); ++it)
-    {
-      assert(*it != NULL);
-      delete *it;
-    }
-
-    for (SlicedVolumes::iterator it = slicedVolumes_.begin(); it != slicedVolumes_.end(); ++it)
-    {
-      assert(*it != NULL);
-      delete *it;
-    }
-
-    for (VolumeLoaders::iterator it = volumeLoaders_.begin(); it != volumeLoaders_.end(); ++it)
-    {
-      assert(*it != NULL);
-      delete *it;
-    }
-  }
-
-
   IWidget& BasicApplicationContext::SetCentralWidget(IWidget* widget)   // Takes ownership
   {
-    viewport_.SetCentralWidget(widget);
+    centralViewport_.SetCentralWidget(widget);
     return *widget;
   }
 
 
-  ISlicedVolume& BasicApplicationContext::AddSlicedVolume(ISlicedVolume* volume)
-  {
-    if (volume == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-    }
-    else
-    {
-      slicedVolumes_.push_back(volume);
-      return *volume;
-    }
-  }
-
-
-  IVolumeLoader& BasicApplicationContext::AddVolumeLoader(IVolumeLoader* loader)
-  {
-    if (loader == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-    }
-    else
-    {
-      volumeLoaders_.push_back(loader);
-      return *loader;
-    }
-  }
-
-
-  IWorldSceneInteractor& BasicApplicationContext::AddInteractor(IWorldSceneInteractor* interactor)
-  {
-    if (interactor == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-    }
-
-    interactors_.push_back(interactor);
-
-    return *interactor;
-  }
-
-
   void BasicApplicationContext::Start()
   {
     oracle_.Start();
 
-    if (viewport_.HasUpdateContent())
+    if (centralViewport_.HasUpdateContent())
     {
       stopped_ = false;
       updateThread_ = boost::thread(UpdateThread, this);
--- a/Applications/BasicApplicationContext.h	Fri Jun 08 14:41:45 2018 +0200
+++ b/Applications/BasicApplicationContext.h	Mon Jun 11 14:01:02 2018 +0200
@@ -35,19 +35,14 @@
   class BasicApplicationContext : public boost::noncopyable
   {
   private:
-    typedef std::list<ISlicedVolume*>          SlicedVolumes;
-    typedef std::list<IVolumeLoader*>          VolumeLoaders;
-    typedef std::list<IWorldSceneInteractor*>  Interactors;
 
     static void UpdateThread(BasicApplicationContext* that);
 
+
     Oracle              oracle_;
     OracleWebService    webService_;
     boost::mutex        viewportMutex_;
-    WidgetViewport      viewport_;
-    SlicedVolumes       slicedVolumes_;
-    VolumeLoaders       volumeLoaders_;
-    Interactors         interactors_;
+    WidgetViewport      centralViewport_;
     boost::thread       updateThread_;
     bool                stopped_;
     unsigned int        updateDelay_;
@@ -62,7 +57,7 @@
     public:
       ViewportLocker(BasicApplicationContext& that) :
         lock_(that.viewportMutex_),
-        viewport_(that.viewport_)
+        viewport_(that.centralViewport_)
       {
       }
 
@@ -75,7 +70,7 @@
     
     BasicApplicationContext(Orthanc::WebServiceParameters& orthanc);
 
-    ~BasicApplicationContext();
+    virtual ~BasicApplicationContext() {}
 
     IWidget& SetCentralWidget(IWidget* widget);   // Takes ownership
 
@@ -84,12 +79,6 @@
       return webService_;
     }
     
-    ISlicedVolume& AddSlicedVolume(ISlicedVolume* volume);
-
-    IVolumeLoader& AddVolumeLoader(IVolumeLoader* loader);
-
-    IWorldSceneInteractor& AddInteractor(IWorldSceneInteractor* interactor);
-
     void Start();
 
     void Stop();
--- a/Applications/IBasicApplication.cpp	Fri Jun 08 14:41:45 2018 +0200
+++ b/Applications/IBasicApplication.cpp	Mon Jun 11 14:01:02 2018 +0200
@@ -30,262 +30,34 @@
 
 namespace OrthancStone
 {
-  // Anonymous namespace to avoid clashes against other compilation modules
-  namespace
-  {
-    class LogStatusBar : public IStatusBar
-    {
-    public:
-      virtual void ClearMessage()
-      {
-      }
-
-      virtual void SetMessage(const std::string& message)
-      {
-        LOG(WARNING) << message;
-      }
-    };
-  }
-
-
-#if ORTHANC_ENABLE_SDL == 1
-  static void DeclareSdlCommandLineOptions(boost::program_options::options_description& options)
-  {
-    // Declare the supported parameters
-    boost::program_options::options_description generic("Generic options");
-    generic.add_options()
-      ("help", "Display this help and exit")
-      ("verbose", "Be verbose in logs")
-      ("orthanc", boost::program_options::value<std::string>()->default_value("http://localhost:8042/"),
-       "URL to the Orthanc server")
-      ("username", "Username for the Orthanc server")
-      ("password", "Password for the Orthanc server")
-      ("https-verify", boost::program_options::value<bool>()->default_value(true), "Check HTTPS certificates")
-      ;
-
-    options.add(generic);
-
-    boost::program_options::options_description sdl("SDL options");
-    sdl.add_options()
-      ("width", boost::program_options::value<int>()->default_value(1024), "Initial width of the SDL window")
-      ("height", boost::program_options::value<int>()->default_value(768), "Initial height of the SDL window")
-      ("opengl", boost::program_options::value<bool>()->default_value(true), "Enable OpenGL in SDL")
-      ;
-
-    options.add(sdl);
-  }
-
-
-  int IBasicApplication::ExecuteWithSdl(IBasicApplication& application,
-                                        int argc, 
-                                        char* argv[])
-  {
-    /******************************************************************
-     * Initialize all the subcomponents of Orthanc Stone
-     ******************************************************************/
-
-    Orthanc::Logging::Initialize();
-    Orthanc::HttpClient::InitializeOpenSsl();
-    Orthanc::HttpClient::GlobalInitialize();
-    SdlWindow::GlobalInitialize();
-
-
-    /******************************************************************
-     * Declare and parse the command-line options of the application
-     ******************************************************************/
+    void IBasicApplication::DeclareStringStartupOption(const std::string& name, const std::string& defaultValue, const std::string& helpText) {
+        StartupOption option;
+        option.name = name;
+        option.defaultValue = defaultValue;
+        option.helpText = helpText = helpText;
+        option.type = StartupOption::string;
 
-    boost::program_options::options_description options;
-    DeclareSdlCommandLineOptions(options);   
-    application.DeclareCommandLineOptions(options);
-
-    boost::program_options::variables_map parameters;
-    bool error = false;
-
-    try
-    {
-      boost::program_options::store(boost::program_options::command_line_parser(argc, argv).
-                                    options(options).run(), parameters);
-      boost::program_options::notify(parameters);    
-    }
-    catch (boost::program_options::error& e)
-    {
-      LOG(ERROR) << "Error while parsing the command-line arguments: " << e.what();
-      error = true;
-    }
-
-
-    /******************************************************************
-     * Configure the application with the command-line parameters
-     ******************************************************************/
-
-    if (error || parameters.count("help")) 
-    {
-      std::cout << std::endl
-                << "Usage: " << argv[0] << " [OPTION]..."
-                << std::endl
-                << "Orthanc, lightweight, RESTful DICOM server for healthcare and medical research."
-                << std::endl << std::endl
-                << "Demonstration application of Orthanc Stone using SDL."
-                << std::endl;
-
-      std::cout << options << "\n";
-      return error ? -1 : 0;
-    }
-
-    if (parameters.count("https-verify") &&
-        !parameters["https-verify"].as<bool>())
-    {
-      LOG(WARNING) << "Turning off verification of HTTPS certificates (unsafe)";
-      Orthanc::HttpClient::ConfigureSsl(false, "");
-    }
-
-    if (parameters.count("verbose"))
-    {
-      Orthanc::Logging::EnableInfoLevel(true);
-    }
-
-    if (!parameters.count("width") ||
-        !parameters.count("height") ||
-        !parameters.count("opengl"))
-    {
-      LOG(ERROR) << "Parameter \"width\", \"height\" or \"opengl\" is missing";
-      return -1;
-    }
-
-    int w = parameters["width"].as<int>();
-    int h = parameters["height"].as<int>();
-    if (w <= 0 || h <= 0)
-    {
-      LOG(ERROR) << "Parameters \"width\" and \"height\" must be positive";
-      return -1;
+        startupOptions_.push_back(option);
     }
 
-    unsigned int width = static_cast<unsigned int>(w);
-    unsigned int height = static_cast<unsigned int>(h);
-    LOG(WARNING) << "Initial display size: " << width << "x" << height;
+    void IBasicApplication::DeclareBoolStartupOption(const std::string& name, bool defaultValue, const std::string& helpText) {
+        StartupOption option;
+        option.name = name;
+        option.defaultValue = (defaultValue ? "true" : "false");
+        option.helpText = helpText = helpText;
+        option.type = StartupOption::boolean;
 
-    bool opengl = parameters["opengl"].as<bool>();
-    if (opengl)
-    {
-      LOG(WARNING) << "OpenGL is enabled, disable it with option \"--opengl=off\" if the application crashes";
-    }
-    else
-    {
-      LOG(WARNING) << "OpenGL is disabled, enable it with option \"--opengl=on\" for best performance";
+        startupOptions_.push_back(option);
     }
 
-    bool success = true;
-    try
-    {
-      /****************************************************************
-       * Initialize the connection to the Orthanc server
-       ****************************************************************/
-
-      Orthanc::WebServiceParameters webService;
-
-      if (parameters.count("orthanc"))
-      {
-        webService.SetUrl(parameters["orthanc"].as<std::string>());
-      }
-
-      if (parameters.count("username"))
-      {
-        webService.SetUsername(parameters["username"].as<std::string>());
-      }
-
-      if (parameters.count("password"))
-      {
-        webService.SetPassword(parameters["password"].as<std::string>());
-      }
-
-      LOG(WARNING) << "URL to the Orthanc REST API: " << webService.GetUrl();
-
-      {
-        OrthancPlugins::OrthancHttpConnection orthanc(webService);
-        if (!MessagingToolbox::CheckOrthancVersion(orthanc))
-        {
-          LOG(ERROR) << "Your version of Orthanc is incompatible with Stone of Orthanc, please upgrade";
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
-        }
-      }
-
-
-      /****************************************************************
-       * Initialize the application
-       ****************************************************************/
-
-      LOG(WARNING) << "Creating the widgets of the application";
-
-      LogStatusBar statusBar;
-      BasicApplicationContext context(webService);
-
-      application.Initialize(context, statusBar, parameters);
+    void IBasicApplication::DeclareIntegerStartupOption(const std::string& name, const int& defaultValue, const std::string& helpText) {
+        StartupOption option;
+        option.name = name;
+        option.defaultValue = std::to_string(defaultValue);
+        option.helpText = helpText = helpText;
+        option.type = StartupOption::integer;
 
-      {
-        BasicApplicationContext::ViewportLocker locker(context);
-        locker.GetViewport().SetStatusBar(statusBar);
-      }
-
-      std::string title = application.GetTitle();
-      if (title.empty())
-      {
-        title = "Stone of Orthanc";
-      }
-
-      {
-        /**************************************************************
-         * Run the application inside a SDL window
-         **************************************************************/
-
-        LOG(WARNING) << "Starting the application";
-
-        SdlWindow window(title.c_str(), width, height, opengl);
-        SdlEngine sdl(window, context);
-
-        {
-          BasicApplicationContext::ViewportLocker locker(context);
-          locker.GetViewport().Register(sdl);  // (*)
-        }
-
-        context.Start();
-        sdl.Run();
-
-        LOG(WARNING) << "Stopping the application";
-
-        // Don't move the "Stop()" command below out of the block,
-        // otherwise the application might crash, because the
-        // "SdlEngine" is an observer of the viewport (*) and the
-        // update thread started by "context.Start()" would call a
-        // destructed object (the "SdlEngine" is deleted with the
-        // lexical scope).
-        context.Stop();
-      }
-
-
-      /****************************************************************
-       * Finalize the application
-       ****************************************************************/
-
-      LOG(WARNING) << "The application has stopped";
-      application.Finalize();
-    }
-    catch (Orthanc::OrthancException& e)
-    {
-      LOG(ERROR) << "EXCEPTION: " << e.What();
-      success = false;
+        startupOptions_.push_back(option);
     }
 
-
-    /******************************************************************
-     * Finalize all the subcomponents of Orthanc Stone
-     ******************************************************************/
-
-    SdlWindow::GlobalFinalize();
-    Orthanc::HttpClient::GlobalFinalize();
-    Orthanc::HttpClient::FinalizeOpenSsl();
-
-    return (success ? 0 : -1);
-  }
-#endif
-
 }
--- a/Applications/IBasicApplication.h	Fri Jun 08 14:41:45 2018 +0200
+++ b/Applications/IBasicApplication.h	Mon Jun 11 14:01:02 2018 +0200
@@ -22,36 +22,62 @@
 #pragma once
 
 #include "BasicApplicationContext.h"
-
 #include <boost/program_options.hpp>
 
-#if ORTHANC_ENABLE_SDL == 1
-#  include <SDL.h>   // Necessary to avoid undefined reference to `SDL_main'
-#endif
-
 namespace OrthancStone
 {
   class IBasicApplication : public boost::noncopyable
   {
+  protected:
+//    struct StartupOptionValue {
+//      enum Type {
+//        boolean,
+//        string,
+//        integer
+//      };
+//      Type type;
+//      std::string value;
+
+//      int asInt() {return std::stoi(value);}
+//      bool asBool() {return value == "true"; }
+//      std::string asString() {return value; }
+//    };
+
+//    struct StartupOptionDefinition {
+//      std::string name;
+//      std::string helpText;
+//      std::string defaultValue;
+//      StartupOptionValue::Type type;
+//    };
+
+//    typedef std::list<StartupOptionDefinition> StartupOptions;
+
+//    StartupOptions startupOptions_;
+
   public:
     virtual ~IBasicApplication()
     {
     }
 
-    virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) = 0;
+    virtual void DeclareStartupOptions(boost::program_options::options_description& options) = 0;
+    virtual void Initialize(IStatusBar& statusBar,
+                            const boost::program_options::variables_map& parameters) = 0;
+
+    virtual BasicApplicationContext& CreateApplicationContext(Orthanc::WebServiceParameters& orthanc) = 0;
+
 
     virtual std::string GetTitle() const = 0;
 
-    virtual void Initialize(BasicApplicationContext& context,
-                            IStatusBar& statusBar,
-                            const boost::program_options::variables_map& parameters) = 0;
+//    virtual void Initialize(BasicApplicationContext& context,
+//                            IStatusBar& statusBar,
+//                            const std::map<std::string, std::string>& startupOptions) = 0;
 
     virtual void Finalize() = 0;
 
-#if ORTHANC_ENABLE_SDL == 1
-    static int ExecuteWithSdl(IBasicApplication& application,
-                              int argc, 
-                              char* argv[]);
-#endif
+//protected:
+//    virtual void DeclareStringStartupOption(const std::string& name, const std::string& defaultValue, const std::string& helpText);
+//    virtual void DeclareIntegerStartupOption(const std::string& name, const int& defaultValue, const std::string& helpText);
+//    virtual void DeclareBoolStartupOption(const std::string& name, bool defaultValue, const std::string& helpText);
   };
+
 }
--- a/Applications/Samples/SampleApplicationBase.h	Fri Jun 08 14:41:45 2018 +0200
+++ b/Applications/Samples/SampleApplicationBase.h	Mon Jun 11 14:01:02 2018 +0200
@@ -21,27 +21,38 @@
 
 #pragma once
 
-#include "../IBasicApplication.h"
+#include "../../Applications/Sdl/BasicSdlApplication.h"
+#include "SampleApplicationContext.h"
 
 namespace OrthancStone
 {
   namespace Samples
   {
-    class SampleApplicationBase : public IBasicApplication
+
+#ifdef ORTHANC_ENABLE_SDL
+    class SampleSdlApplicationBase : BasicSdlApplication {
+    private:
+      std::unique_ptr<SampleApplicationContext> context_;
+
+      BasicApplicationContext& CreateApplicationContext(Orthanc::WebServiceParameters& orthanc) {
+        context_.reset(new SampleApplicationContext(orthanc));
+      }
+    };
+
+    typedef SampleApplicationBase_ SampleSdlApplicationBase;
+#else
+
+#endif
+
+    class SampleApplicationBase : public SampleApplicationBase_
     {
     public:
       virtual std::string GetTitle() const
       {
         return "Stone of Orthanc - Sample";
       }
-
-      virtual void DeclareCommandLineOptions(boost::program_options::options_description& options)
-      {
-      }
+    };
 
-      virtual void Finalize()
-      {
-      }
-    };
+
   }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Samples/SampleApplicationContext.cpp	Mon Jun 11 14:01:02 2018 +0200
@@ -0,0 +1,94 @@
+/**
+ * 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 "SampleApplicationContext.h"
+
+namespace OrthancStone
+{
+  SampleApplicationContext::SampleApplicationContext(Orthanc::WebServiceParameters& orthanc) :
+    BaseApplicationContext(orthanc)
+  {
+  }
+
+
+  SampleApplicationContext::~SampleApplicationContext()
+  {
+    for (Interactors::iterator it = interactors_.begin(); it != interactors_.end(); ++it)
+    {
+      assert(*it != NULL);
+      delete *it;
+    }
+
+    for (SlicedVolumes::iterator it = slicedVolumes_.begin(); it != slicedVolumes_.end(); ++it)
+    {
+      assert(*it != NULL);
+      delete *it;
+    }
+
+    for (VolumeLoaders::iterator it = volumeLoaders_.begin(); it != volumeLoaders_.end(); ++it)
+    {
+      assert(*it != NULL);
+      delete *it;
+    }
+  }
+
+
+  ISlicedVolume& SampleApplicationContext::AddSlicedVolume(ISlicedVolume* volume)
+  {
+    if (volume == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+    }
+    else
+    {
+      slicedVolumes_.push_back(volume);
+      return *volume;
+    }
+  }
+
+
+  IVolumeLoader& SampleApplicationContext::AddVolumeLoader(IVolumeLoader* loader)
+  {
+    if (loader == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+    }
+    else
+    {
+      volumeLoaders_.push_back(loader);
+      return *loader;
+    }
+  }
+
+
+  IWorldSceneInteractor& SampleApplicationContext::AddInteractor(IWorldSceneInteractor* interactor)
+  {
+    if (interactor == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+    }
+
+    interactors_.push_back(interactor);
+
+    return *interactor;
+  }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Samples/SampleApplicationContext.h	Mon Jun 11 14:01:02 2018 +0200
@@ -0,0 +1,60 @@
+/**
+ * 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 "../BasicApplicationContext.h"
+
+#include <list>
+#include <boost/thread.hpp>
+
+
+namespace OrthancStone
+{
+  class SampleApplicationContext : public BasicApplicationContext
+  {
+  private:
+    typedef std::list<ISlicedVolume*>          SlicedVolumes;  // this is actually used by the samples and shall be moved to a SampleApplicationContext
+    typedef std::list<IVolumeLoader*>          VolumeLoaders;
+    typedef std::list<IWorldSceneInteractor*>  Interactors;
+
+    SlicedVolumes       slicedVolumes_;
+    VolumeLoaders       volumeLoaders_;
+    Interactors         interactors_;
+
+  public:
+
+    SampleApplicationContext(Orthanc::WebServiceParameters& orthanc);
+
+    virtual ~SampleApplicationContext();
+
+    ISlicedVolume& AddSlicedVolume(ISlicedVolume* volume);
+
+    IVolumeLoader& AddVolumeLoader(IVolumeLoader* loader);
+
+    IWorldSceneInteractor& AddInteractor(IWorldSceneInteractor* interactor);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Sdl/BasicSdlApplication.cpp	Mon Jun 11 14:01:02 2018 +0200
@@ -0,0 +1,316 @@
+/**
+ * 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/>.
+ **/
+
+
+#if ORTHANC_ENABLE_SDL != 1
+#error this file shall be included only with the ORTHANC_ENABLE_SDL set to 1
+#endif
+
+#include "BasicSdlApplication.h"
+
+#include "../../Framework/Toolbox/MessagingToolbox.h"
+#include "SdlEngine.h"
+
+#include <Core/Logging.h>
+#include <Core/HttpClient.h>
+#include <Plugins/Samples/Common/OrthancHttpConnection.h>
+
+namespace OrthancStone
+{
+  // Anonymous namespace to avoid clashes against other compilation modules
+  namespace
+  {
+    class LogStatusBar : public IStatusBar
+    {
+    public:
+      virtual void ClearMessage()
+      {
+      }
+
+      virtual void SetMessage(const std::string& message)
+      {
+        LOG(WARNING) << message;
+      }
+    };
+  }
+
+
+  static void DeclareSdlCommandLineOptions(boost::program_options::options_description& options)
+  {
+    // Declare the supported parameters
+    boost::program_options::options_description generic("Generic options");
+    generic.add_options()
+      ("help", "Display this help and exit")
+      ("verbose", "Be verbose in logs")
+      ("orthanc", boost::program_options::value<std::string>()->default_value("http://localhost:8042/"),
+       "URL to the Orthanc server")
+      ("username", "Username for the Orthanc server")
+      ("password", "Password for the Orthanc server")
+      ("https-verify", boost::program_options::value<bool>()->default_value(true), "Check HTTPS certificates")
+      ;
+
+    options.add(generic);
+
+    boost::program_options::options_description sdl("SDL options");
+    sdl.add_options()
+      ("width", boost::program_options::value<int>()->default_value(1024), "Initial width of the SDL window")
+      ("height", boost::program_options::value<int>()->default_value(768), "Initial height of the SDL window")
+      ("opengl", boost::program_options::value<bool>()->default_value(true), "Enable OpenGL in SDL")
+      ;
+
+    options.add(sdl);
+  }
+
+//  void BasicSdlApplication::DeclareCommandLineOptions(boost::program_options::options_description &options) {
+//    boost::program_options::options_description app("Application specifi options");
+
+//    for (IBasicApplication::StartupOptions::const_iterator it = startupOptions_.begin(); it != startupOptions_.end(); it++) {
+//      switch (it->type) {
+//      case IBasicApplication::StartupOptionValue::boolean:
+//        app.add_options()
+//            (it->name.c_str(), boost::program_options::value<int>()->default_value(std::stoi(it->defaultValue)), it->helpText.c_str());
+//        break;
+//      case IBasicApplication::StartupOptionValue::integer:
+//        app.add_options()
+//            (it->name.c_str(), boost::program_options::value<bool>()->default_value(it->defaultValue == "true"), it->helpText.c_str());
+//        break;
+//      case IBasicApplication::StartupOptionValue::string:
+//        app.add_options()
+//            (it->name.c_str(), boost::program_options::value<std::string>()->default_value(it->defaultValue), it->helpText.c_str());
+//        break;
+//      default:
+//        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+//      }
+//    }
+//    options.add(app);
+//  }
+
+  int BasicSdlApplication::ExecuteWithSdl(BasicSdlApplication& application,
+                                        int argc,
+                                        char* argv[])
+  {
+    /******************************************************************
+     * Initialize all the subcomponents of Orthanc Stone
+     ******************************************************************/
+
+    Orthanc::Logging::Initialize();
+    Orthanc::HttpClient::InitializeOpenSsl();
+    Orthanc::HttpClient::GlobalInitialize();
+    SdlWindow::GlobalInitialize();
+
+
+    /******************************************************************
+     * Declare and parse the command-line options of the application
+     ******************************************************************/
+
+    boost::program_options::options_description options;
+    DeclareSdlCommandLineOptions(options);
+    application.DeclareStartupOptions(options);
+
+    boost::program_options::variables_map parameters;
+    bool error = false;
+
+    try
+    {
+      boost::program_options::store(boost::program_options::command_line_parser(argc, argv).
+                                    options(options).run(), parameters);
+      boost::program_options::notify(parameters);
+    }
+    catch (boost::program_options::error& e)
+    {
+      LOG(ERROR) << "Error while parsing the command-line arguments: " << e.what();
+      error = true;
+    }
+
+
+    /******************************************************************
+     * Configure the application with the command-line parameters
+     ******************************************************************/
+
+    if (error || parameters.count("help"))
+    {
+      std::cout << std::endl
+                << "Usage: " << argv[0] << " [OPTION]..."
+                << std::endl
+                << "Orthanc, lightweight, RESTful DICOM server for healthcare and medical research."
+                << std::endl << std::endl
+                << "Demonstration application of Orthanc Stone using SDL."
+                << std::endl;
+
+      std::cout << options << "\n";
+      return error ? -1 : 0;
+    }
+
+    if (parameters.count("https-verify") &&
+        !parameters["https-verify"].as<bool>())
+    {
+      LOG(WARNING) << "Turning off verification of HTTPS certificates (unsafe)";
+      Orthanc::HttpClient::ConfigureSsl(false, "");
+    }
+
+    if (parameters.count("verbose"))
+    {
+      Orthanc::Logging::EnableInfoLevel(true);
+    }
+
+    if (!parameters.count("width") ||
+        !parameters.count("height") ||
+        !parameters.count("opengl"))
+    {
+      LOG(ERROR) << "Parameter \"width\", \"height\" or \"opengl\" is missing";
+      return -1;
+    }
+
+    int w = parameters["width"].as<int>();
+    int h = parameters["height"].as<int>();
+    if (w <= 0 || h <= 0)
+    {
+      LOG(ERROR) << "Parameters \"width\" and \"height\" must be positive";
+      return -1;
+    }
+
+    unsigned int width = static_cast<unsigned int>(w);
+    unsigned int height = static_cast<unsigned int>(h);
+    LOG(WARNING) << "Initial display size: " << width << "x" << height;
+
+    bool opengl = parameters["opengl"].as<bool>();
+    if (opengl)
+    {
+      LOG(WARNING) << "OpenGL is enabled, disable it with option \"--opengl=off\" if the application crashes";
+    }
+    else
+    {
+      LOG(WARNING) << "OpenGL is disabled, enable it with option \"--opengl=on\" for best performance";
+    }
+
+    bool success = true;
+    try
+    {
+      /****************************************************************
+       * Initialize the connection to the Orthanc server
+       ****************************************************************/
+
+      Orthanc::WebServiceParameters webService;
+
+      if (parameters.count("orthanc"))
+      {
+        webService.SetUrl(parameters["orthanc"].as<std::string>());
+      }
+
+      if (parameters.count("username"))
+      {
+        webService.SetUsername(parameters["username"].as<std::string>());
+      }
+
+      if (parameters.count("password"))
+      {
+        webService.SetPassword(parameters["password"].as<std::string>());
+      }
+
+      LOG(WARNING) << "URL to the Orthanc REST API: " << webService.GetUrl();
+
+      {
+        OrthancPlugins::OrthancHttpConnection orthanc(webService);
+        if (!MessagingToolbox::CheckOrthancVersion(orthanc))
+        {
+          LOG(ERROR) << "Your version of Orthanc is incompatible with Stone of Orthanc, please upgrade";
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
+        }
+      }
+
+
+      /****************************************************************
+       * Initialize the application
+       ****************************************************************/
+
+      LOG(WARNING) << "Creating the widgets of the application";
+
+      LogStatusBar statusBar;
+      BasicApplicationContext& context = application.CreateApplicationContext(webService);
+
+      application.Initialize(statusBar, parameters);
+
+      {
+        BasicApplicationContext::ViewportLocker locker(context);
+        locker.GetViewport().SetStatusBar(statusBar);
+      }
+
+      std::string title = application.GetTitle();
+      if (title.empty())
+      {
+        title = "Stone of Orthanc";
+      }
+
+      {
+        /**************************************************************
+         * Run the application inside a SDL window
+         **************************************************************/
+
+        LOG(WARNING) << "Starting the application";
+
+        SdlWindow window(title.c_str(), width, height, opengl);
+        SdlEngine sdl(window, context);
+
+        {
+          BasicApplicationContext::ViewportLocker locker(context);
+          locker.GetViewport().Register(sdl);  // (*)
+        }
+
+        context.Start();
+        sdl.Run();
+
+        LOG(WARNING) << "Stopping the application";
+
+        // Don't move the "Stop()" command below out of the block,
+        // otherwise the application might crash, because the
+        // "SdlEngine" is an observer of the viewport (*) and the
+        // update thread started by "context.Start()" would call a
+        // destructed object (the "SdlEngine" is deleted with the
+        // lexical scope).
+        context.Stop();
+      }
+
+
+      /****************************************************************
+       * Finalize the application
+       ****************************************************************/
+
+      LOG(WARNING) << "The application has stopped";
+      application.Finalize();
+    }
+    catch (Orthanc::OrthancException& e)
+    {
+      LOG(ERROR) << "EXCEPTION: " << e.What();
+      success = false;
+    }
+
+
+    /******************************************************************
+     * Finalize all the subcomponents of Orthanc Stone
+     ******************************************************************/
+
+    SdlWindow::GlobalFinalize();
+    Orthanc::HttpClient::GlobalFinalize();
+    Orthanc::HttpClient::FinalizeOpenSsl();
+
+    return (success ? 0 : -1);
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Sdl/BasicSdlApplication.h	Mon Jun 11 14:01:02 2018 +0200
@@ -0,0 +1,49 @@
+/**
+ * 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 "../IBasicApplication.h"
+
+#include <boost/program_options.hpp>
+
+#if ORTHANC_ENABLE_SDL != 1
+#error this file shall be included only with the ORTHANC_ENABLE_SDL set to 1
+#endif
+
+#include <SDL.h>   // Necessary to avoid undefined reference to `SDL_main'
+
+namespace OrthancStone
+{
+  class BasicSdlApplication : public IBasicApplication
+  {
+  public:
+    virtual ~BasicSdlApplication()
+    {
+    }
+
+    static int ExecuteWithSdl(BasicSdlApplication& application,
+                              int argc,
+                              char* argv[]);
+  };
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Wasm/BasicWasmApplication.h	Mon Jun 11 14:01:02 2018 +0200
@@ -0,0 +1,45 @@
+/**
+ * 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 "../IBasicApplication.h"
+
+#include <boost/program_options.hpp>
+
+#if ORTHANC_ENABLE_SDL == 1
+#error this file shall be included only with the ORTHANC_ENABLE_SDL set to 0
+#endif
+
+namespace OrthancStone
+{
+  class BasicWasmApplication : public IBasicApplication
+  {
+  public:
+    virtual ~BasicWasmApplication()
+    {
+    }
+
+    //static int ExecuteWithWasm(BasicWasmApplication& application);
+  };
+
+}
--- a/Platforms/Generic/CMakeLists.txt	Fri Jun 08 14:41:45 2018 +0200
+++ b/Platforms/Generic/CMakeLists.txt	Mon Jun 11 14:01:02 2018 +0200
@@ -45,9 +45,13 @@
 ## Build all the sample applications
 #####################################################################
 
-macro(BuildSample Target Sample)
+macro(BuildSample Target Header Sample)
   add_executable(${Target}
     ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleMainSdl.cpp
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleApplicationContext.cpp
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleInteractor.h
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleApplicationBase.h
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/${Header}
     ${APPLICATIONS_SOURCES}
     )
   set_target_properties(${Target} PROPERTIES COMPILE_DEFINITIONS ORTHANC_STONE_SAMPLE=${Sample})
@@ -57,10 +61,10 @@
 
 # TODO - Re-enable all these samples!
 
-BuildSample(OrthancStoneEmpty 1)
-BuildSample(OrthancStoneTestPattern 2)
-BuildSample(OrthancStoneSingleFrame 3)
-BuildSample(OrthancStoneSingleVolume 4)
+BuildSample(OrthancStoneEmpty EmptyApplication.h 1)
+BuildSample(OrthancStoneTestPattern TestPatternApplication.h 2)
+BuildSample(OrthancStoneSingleFrame SingleFrameApplication.h 3)
+BuildSample(OrthancStoneSingleVolume SingleVolumeApplication.h 4)
 #BuildSample(OrthancStoneBasicPetCtFusion 5)
 #BuildSample(OrthancStoneSynchronizedSeries 6)
 #BuildSample(OrthancStoneLayoutPetCtFusion 7)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Platforms/WebAssembly/WasmWebService.cpp	Mon Jun 11 14:01:02 2018 +0200
@@ -0,0 +1,90 @@
+#include "WasmWebService.h"
+
+#include <emscripten/emscripten.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  extern void WasmWebService_ScheduleGetRequest(void* callback,
+                                                const char* uri,
+                                                void* payload);
+  
+  extern void WasmWebService_SchedulePostRequest(void* callback,
+                                                 const char* uri,
+                                                 const void* body,
+                                                 size_t bodySize,
+                                                 void* payload);
+
+  void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifyError(void* callback,
+                                                       const char* uri,
+                                                       void* payload)
+  {
+    if (callback == NULL)
+    {
+      throw;
+    }
+    else
+    {
+      reinterpret_cast<OrthancStone::IWebService::ICallback*>(callback)->
+        NotifyError(uri, reinterpret_cast<Orthanc::IDynamicObject*>(payload));
+    }
+  }
+
+  void EMSCRIPTEN_KEEPALIVE WasmWebService_NotifySuccess(void* callback,
+                                                         const char* uri,
+                                                         const void* body,
+                                                         size_t bodySize,
+                                                         void* payload)
+  {
+    if (callback == NULL)
+    {
+      throw;
+    }
+    else
+    {
+      reinterpret_cast<OrthancStone::IWebService::ICallback*>(callback)->
+        NotifySuccess(uri, body, bodySize, reinterpret_cast<Orthanc::IDynamicObject*>(payload)); 
+   }
+  }
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
+namespace OrthancStone
+{
+  void WasmWebService::SetBaseUrl(const std::string base)
+  {
+    // Make sure the base url ends with "/"
+    if (base.empty() ||
+        base[base.size() - 1] != '/')
+    {
+      base_ = base + "/";
+    }
+    else
+    {
+      base_ = base;
+    }
+  }
+
+  void WasmWebService::ScheduleGetRequest(ICallback& callback,
+                                          const std::string& uri,
+                                          Orthanc::IDynamicObject* payload)
+  {
+    std::string url = base_ + uri;
+    WasmWebService_ScheduleGetRequest(&callback, url.c_str(), payload);
+  }
+
+  void WasmWebService::SchedulePostRequest(ICallback& callback,
+                                           const std::string& uri,
+                                           const std::string& body,
+                                           Orthanc::IDynamicObject* payload)
+  {
+    std::string url = base_ + uri;
+    WasmWebService_SchedulePostRequest(&callback, url.c_str(),
+                                       body.c_str(), body.size(), payload);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Platforms/WebAssembly/WasmWebService.h	Mon Jun 11 14:01:02 2018 +0200
@@ -0,0 +1,44 @@
+#pragma once
+
+#include <Framework/Toolbox/IWebService.h>
+
+namespace OrthancStone
+{
+  class WasmWebService : public IWebService
+  {
+  private:
+    std::string  base_;
+
+    // Private constructor => Singleton design pattern
+    WasmWebService() :
+      base_("../../")
+    {
+    }
+
+  public:
+    static WasmWebService& GetInstance()
+    {
+      static WasmWebService instance;
+      return instance;
+    }
+
+    void SetBaseUrl(const std::string base);
+
+    virtual void ScheduleGetRequest(ICallback& callback,
+                                    const std::string& uri,
+                                    Orthanc::IDynamicObject* payload);
+
+    virtual void SchedulePostRequest(ICallback& callback,
+                                     const std::string& uri,
+                                     const std::string& body,
+                                     Orthanc::IDynamicObject* payload);
+
+    virtual void Start()
+    {
+    }
+    
+    virtual void Stop()
+    {
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Platforms/WebAssembly/WasmWebService.js	Mon Jun 11 14:01:02 2018 +0200
@@ -0,0 +1,51 @@
+mergeInto(LibraryManager.library, {
+  WasmWebService_ScheduleGetRequest: function(callback, url, payload) {
+    // Directly use XMLHttpRequest (no jQuery) to retrieve the raw binary data
+    // http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/
+    var xhr = new XMLHttpRequest();
+    var tmp = UTF8ToString(url);
+    xhr.open('GET', tmp, true);
+    xhr.responseType = 'arraybuffer';
+
+    xhr.onreadystatechange = function() {
+      if (this.readyState == XMLHttpRequest.DONE) {
+        if (xhr.status === 200) {
+          // TODO - Is "new Uint8Array()" necessary? This copies the
+          // answer to the WebAssembly stack, hence necessitating
+          // increasing the TOTAL_STACK parameter of Emscripten
+          WasmWebService_NotifySuccess(callback, tmp, new Uint8Array(this.response),
+                                       this.response.byteLength, payload);
+        } else {
+          WasmWebService_NotifyError(callback, tmp, payload);
+        }
+      }
+    }
+    
+    xhr.send();
+  },
+
+  WasmWebService_SchedulePostRequest: function(callback, url, body, bodySize, payload) {
+    var xhr = new XMLHttpRequest();
+    var tmp = UTF8ToString(url);
+    xhr.open('POST', tmp, true);
+    xhr.responseType = 'arraybuffer';
+    xhr.setRequestHeader('Content-type', 'application/octet-stream');
+    
+    xhr.onreadystatechange = function() {
+      if (this.readyState == XMLHttpRequest.DONE) {
+        if (xhr.status === 200) {
+          WasmWebService_NotifySuccess(callback, tmp, new Uint8Array(this.response),
+                                       this.response.byteLength, payload);
+        } else {
+          WasmWebService_NotifyError(callback, tmp, payload);
+        }
+      }
+    }
+
+    xhr.send(new Uint8ClampedArray(HEAPU8.buffer, body, bodySize));
+  },
+
+  ScheduleRedraw: function() {
+    ScheduleRedraw();
+  }
+});
--- a/Resources/CMake/OrthancStoneConfiguration.cmake	Fri Jun 08 14:41:45 2018 +0200
+++ b/Resources/CMake/OrthancStoneConfiguration.cmake	Mon Jun 11 14:01:02 2018 +0200
@@ -135,6 +135,10 @@
 ## All the source files required to build Stone of Orthanc
 #####################################################################
 
+set(APPLICATIONS_SOURCES
+    ${ORTHANC_STONE_ROOT}/Applications/IBasicApplication.h
+    )
+
 if (NOT ORTHANC_SANDBOXED)
   set(PLATFORM_SOURCES
     ${ORTHANC_STONE_ROOT}/Platforms/Generic/WebServiceGetCommand.cpp
@@ -142,14 +146,18 @@
     ${ORTHANC_STONE_ROOT}/Platforms/Generic/Oracle.cpp
     )
 
-  set(APPLICATIONS_SOURCES
+  list(APPEND APPLICATIONS_SOURCES
     ${ORTHANC_STONE_ROOT}/Applications/BasicApplicationContext.cpp
-    ${ORTHANC_STONE_ROOT}/Applications/IBasicApplication.cpp
+    ${ORTHANC_STONE_ROOT}/Applications/Sdl/BasicSdlApplication.cpp
     ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlEngine.cpp
     ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlCairoSurface.cpp
     ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlOrthancSurface.cpp
     ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlWindow.cpp
     )
+else()
+  list(APPEND APPLICATIONS_SOURCES
+    ${ORTHANC_STONE_ROOT}/Applications/Wasm/BasicWasmApplication.cpp
+    )
 endif()
 
 list(APPEND ORTHANC_STONE_SOURCES
@@ -222,3 +230,4 @@
   ${SDL_SOURCES}
   ${BOOST_EXTENDED_SOURCES}
   )
+