changeset 274:dc1beee33134 am-2

split SdlApplication into NativeApplication and SdlApplication
author am@osimis.io
date Fri, 24 Aug 2018 13:52:55 +0200
parents f21ba2468570
children 58e23e0dd86a
files Applications/Generic/BasicNativeApplication.cpp Applications/Generic/BasicNativeApplication.h Applications/Generic/BasicNativeApplicationContext.cpp Applications/Generic/BasicNativeApplicationContext.h Applications/IBasicApplication.h Applications/Samples/SampleApplicationBase.h Applications/Samples/SampleMainSdl.cpp Applications/Samples/SimpleViewerApplication.h Applications/Sdl/BasicSdlApplication.cpp Applications/Sdl/BasicSdlApplication.h Applications/Sdl/BasicSdlApplicationContext.cpp Applications/Sdl/BasicSdlApplicationContext.h Applications/Sdl/SdlEngine.cpp Applications/Sdl/SdlEngine.h Platforms/Generic/OracleWebService.h Platforms/Generic/WebServiceCommandBase.cpp Platforms/Generic/WebServiceCommandBase.h Platforms/Generic/WebServiceGetCommand.cpp Platforms/Generic/WebServiceGetCommand.h Platforms/Generic/WebServicePostCommand.cpp Platforms/Generic/WebServicePostCommand.h Platforms/Wasm/CMakeLists.txt Resources/CMake/OrthancStoneConfiguration.cmake Resources/CMake/OrthancStoneParameters.cmake Resources/CMake/QtConfiguration.cmake
diffstat 24 files changed, 553 insertions(+), 482 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Generic/BasicNativeApplication.cpp	Fri Aug 24 13:52:55 2018 +0200
@@ -0,0 +1,251 @@
+/**
+ * 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_NATIVE != 1
+#error this file shall be included only with the ORTHANC_ENABLE_NATIVE set to 1
+#endif
+
+#include "BasicNativeApplication.h"
+#include "BasicNativeApplicationContext.h"
+#include <boost/program_options.hpp>
+
+#include "../../Framework/Toolbox/MessagingToolbox.h"
+
+#include <Core/Logging.h>
+#include <Core/HttpClient.h>
+#include <Core/Toolbox.h>
+#include <Plugins/Samples/Common/OrthancHttpConnection.h>
+#include "../../Platforms/Generic/OracleWebService.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;
+      }
+    };
+  }
+
+  int BasicNativeApplication::Execute(MessageBroker& broker,
+                                   IBasicApplication& application,
+                                   int argc,
+                                   char* argv[])
+  {
+    /******************************************************************
+     * Initialize all the subcomponents of Orthanc Stone
+     ******************************************************************/
+
+    Orthanc::Logging::Initialize();
+    Orthanc::Toolbox::InitializeOpenSsl();
+    Orthanc::HttpClient::GlobalInitialize();
+
+    Initialize();
+
+    /******************************************************************
+     * Declare and parse the command-line options of the application
+     ******************************************************************/
+
+    boost::program_options::options_description options;
+    DeclareCommandLineOptions(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 webServiceParameters;
+
+      if (parameters.count("orthanc"))
+      {
+        webServiceParameters.SetUrl(parameters["orthanc"].as<std::string>());
+      }
+
+      if (parameters.count("username"))
+      {
+        webServiceParameters.SetUsername(parameters["username"].as<std::string>());
+      }
+
+      if (parameters.count("password"))
+      {
+        webServiceParameters.SetPassword(parameters["password"].as<std::string>());
+      }
+
+      LOG(WARNING) << "URL to the Orthanc REST API: " << webServiceParameters.GetUrl();
+
+      {
+        OrthancPlugins::OrthancHttpConnection orthanc(webServiceParameters);
+        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;
+
+      BasicNativeApplicationContext context;
+      Oracle oracle(4); // use 4 threads to download content
+      OracleWebService webService(broker, oracle, webServiceParameters, context);
+      context.SetWebService(webService);
+
+      application.Initialize(&context, statusBar, parameters);
+
+      {
+        BasicNativeApplicationContext::GlobalMutexLocker locker(context);
+        context.SetCentralWidget(application.GetCentralWidget());
+        context.GetCentralViewport().SetStatusBar(statusBar);
+      }
+
+      std::string title = application.GetTitle();
+      if (title.empty())
+      {
+        title = "Stone of Orthanc";
+      }
+
+      /****************************************************************
+       * Run the application
+       ****************************************************************/
+
+      Run(context, title, width, height, opengl);
+
+
+      /****************************************************************
+       * 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
+     ******************************************************************/
+
+    Finalize();
+    Orthanc::HttpClient::GlobalFinalize();
+    Orthanc::Toolbox::FinalizeOpenSsl();
+
+    return (success ? 0 : -1);
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Generic/BasicNativeApplication.h	Fri Aug 24 13:52:55 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 "../IBasicApplication.h"
+
+#if ORTHANC_ENABLE_NATIVE != 1
+#error this file shall be included only with the ORTHANC_ENABLE_NATIVE set to 1
+#endif
+
+namespace OrthancStone
+{
+  class BasicNativeApplicationContext;
+
+  class BasicNativeApplication
+  {
+  public:
+
+    int Execute(MessageBroker& broker,
+                IBasicApplication& application,
+                int argc,
+                char* argv[]);
+
+    virtual void Initialize() = 0;
+    virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) = 0;
+    virtual void Run(BasicNativeApplicationContext& context, const std::string& title, unsigned int width, unsigned int height, bool enableOpenGl) = 0;
+    virtual void Finalize() = 0;
+  };
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Generic/BasicNativeApplicationContext.cpp	Fri Aug 24 13:52:55 2018 +0200
@@ -0,0 +1,80 @@
+/**
+ * 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 "BasicNativeApplicationContext.h"
+#include "../../Platforms/Generic/OracleWebService.h"
+
+namespace OrthancStone
+{
+  IWidget& BasicNativeApplicationContext::SetCentralWidget(IWidget* widget)   // Takes ownership
+  {
+    centralViewport_->SetCentralWidget(widget);
+    return *widget;
+  }
+
+
+  void BasicNativeApplicationContext::UpdateThread(BasicNativeApplicationContext* that)
+  {
+    while (!that->stopped_)
+    {
+      {
+        GlobalMutexLocker locker(*that);
+        that->GetCentralViewport().UpdateContent();
+      }
+      
+      boost::this_thread::sleep(boost::posix_time::milliseconds(that->updateDelay_));
+    }
+  }
+  
+
+  BasicNativeApplicationContext::BasicNativeApplicationContext() : // Orthanc::WebServiceParameters& orthanc, WidgetViewport* centralViewport) :
+    centralViewport_(new OrthancStone::WidgetViewport()),
+    stopped_(true),
+    updateDelay_(100)   // By default, 100ms between each refresh of the content
+  {
+    srand(time(NULL)); 
+  }
+
+
+  void BasicNativeApplicationContext::Start()
+  {
+    dynamic_cast<OracleWebService*>(webService_)->Start();
+
+    if (centralViewport_->HasUpdateContent())
+    {
+      stopped_ = false;
+      updateThread_ = boost::thread(UpdateThread, this);
+    }
+  }
+
+
+  void BasicNativeApplicationContext::Stop()
+  {
+    stopped_ = true;
+    
+    if (updateThread_.joinable())
+    {
+      updateThread_.join();
+    }
+    
+    dynamic_cast<OracleWebService*>(webService_)->Stop();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Generic/BasicNativeApplicationContext.h	Fri Aug 24 13:52:55 2018 +0200
@@ -0,0 +1,72 @@
+/**
+ * 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 <list>
+#include <boost/thread.hpp>
+#include "../BasicApplicationContext.h"
+
+namespace OrthancStone
+{
+  class BasicNativeApplicationContext : public BasicApplicationContext
+  {
+  private:
+
+    static void UpdateThread(BasicNativeApplicationContext* that);
+
+    boost::mutex        globalMutex_;
+    std::unique_ptr<WidgetViewport>      centralViewport_;
+    boost::thread       updateThread_;
+    bool                stopped_;
+    unsigned int        updateDelay_;
+
+  public:
+    class GlobalMutexLocker: public boost::noncopyable
+    {
+      boost::mutex::scoped_lock  lock_;
+    public:
+      GlobalMutexLocker(BasicNativeApplicationContext& that):
+        lock_(that.globalMutex_)
+      {}
+    };
+
+    BasicNativeApplicationContext();
+
+    virtual ~BasicNativeApplicationContext() {}
+
+    virtual IWidget& SetCentralWidget(IWidget* widget);   // Takes ownership
+    IViewport& GetCentralViewport() {return *(centralViewport_.get());}
+
+    void Start();
+
+    void Stop();
+
+    void SetUpdateDelay(unsigned int delay)  // In milliseconds
+    {
+      updateDelay_ = delay;
+    }
+  };
+}
--- a/Applications/IBasicApplication.h	Fri Aug 24 11:26:59 2018 +0200
+++ b/Applications/IBasicApplication.h	Fri Aug 24 13:52:55 2018 +0200
@@ -41,7 +41,7 @@
     virtual void Initialize(BasicApplicationContext* context,
                             IStatusBar& statusBar,
                             const boost::program_options::variables_map& parameters) = 0;
-#if ORTHANC_ENABLE_SDL==0
+#if ORTHANC_ENABLE_WASM==1
     virtual void InitializeWasm() {}  // specific initialization when the app is running in WebAssembly.  This is called after the other Initialize()
 #endif
 
--- a/Applications/Samples/SampleApplicationBase.h	Fri Aug 24 11:26:59 2018 +0200
+++ b/Applications/Samples/SampleApplicationBase.h	Fri Aug 24 13:52:55 2018 +0200
@@ -21,48 +21,13 @@
 
 #pragma once
 
-//#if ORTHANC_ENABLE_SDL==1
-//#include "../../Applications/Sdl/BasicSdlApplication.h"
-//#else
-//#include "../../Applications/Wasm/BasicWasmApplication.h"
-//#endif
 #include "../../Applications/IBasicApplication.h"
 #include "../../Framework/Viewport/WidgetViewport.h"
-//#include "SampleApplicationContext.h"
 
 namespace OrthancStone
 {
   namespace Samples
   {
-
-//#if ORTHANC_ENABLE_SDL==1
-//    class SampleSdlApplicationBase : public BasicSdlApplication {
-//    protected:
-//    public:
-//      virtual BasicApplicationContext& CreateApplicationContext(Orthanc::WebServiceParameters& orthanc, WidgetViewport* centralViewport) {
-//        context_.reset(new SampleApplicationContext(orthanc, centralViewport));
-
-//        return *context_;
-//      }
-//    };
-
-//    typedef SampleSdlApplicationBase SampleApplicationBase_;
-//#else
-//    class SampleWasmApplicationBase : public BasicWasmApplication {
-//    protected:
-//      std::unique_ptr<SampleApplicationContext> context_;
-//    public:
-//      virtual BasicApplicationContext& CreateApplicationContext(IWebService& orthancWebService, std::shared_ptr<WidgetViewport> centralViewport) {
-//        context_.reset(new SampleApplicationContext(orthancWebService));
-//        return *context_;
-//      }
-
-//    };
-
-//    typedef SampleWasmApplicationBase SampleApplicationBase_;
-
-//#endif
-
     class SampleApplicationBase : public IBasicApplication
     {
     public:
@@ -74,7 +39,5 @@
       virtual void CustomInitialize() {}
 
     };
-
-
   }
 }
--- a/Applications/Samples/SampleMainSdl.cpp	Fri Aug 24 11:26:59 2018 +0200
+++ b/Applications/Samples/SampleMainSdl.cpp	Fri Aug 24 13:52:55 2018 +0200
@@ -28,5 +28,6 @@
   OrthancStone::MessageBroker broker;
   Application application(broker);
 
-  return OrthancStone::BasicSdlApplication::ExecuteWithSdl(broker, application, argc, argv);
+  OrthancStone::BasicSdlApplication sdlApplication;
+  return sdlApplication.Execute(broker, application, argc, argv);
 }
--- a/Applications/Samples/SimpleViewerApplication.h	Fri Aug 24 11:26:59 2018 +0200
+++ b/Applications/Samples/SimpleViewerApplication.h	Fri Aug 24 13:52:55 2018 +0200
@@ -332,11 +332,11 @@
         }
       }
 
-#if ORTHANC_ENABLE_SDL==0
+#if ORTHANC_ENABLE_WASM==1
       virtual void InitializeWasm() {
 
         AttachWidgetToWasmViewport("canvas", thumbnailsLayout_);
-        AttachWidgetToWasmViewport("canvas2", mainViewport_);
+        AttachWidgetToWasmViewport("canvas2", mainWidget_);
       }
 #endif
 
--- a/Applications/Sdl/BasicSdlApplication.cpp	Fri Aug 24 11:26:59 2018 +0200
+++ b/Applications/Sdl/BasicSdlApplication.cpp	Fri Aug 24 13:52:55 2018 +0200
@@ -37,266 +37,71 @@
 
 namespace OrthancStone
 {
-  // Anonymous namespace to avoid clashes against other compilation modules
-  namespace
+  void BasicSdlApplication::Initialize()
   {
-    class LogStatusBar : public IStatusBar
-    {
-    public:
-      virtual void ClearMessage()
-      {
-      }
-
-      virtual void SetMessage(const std::string& message)
-      {
-        LOG(WARNING) << message;
-      }
-    };
+    SdlWindow::GlobalInitialize();
   }
 
-
-  static void DeclareSdlCommandLineOptions(boost::program_options::options_description& options)
+  void BasicSdlApplication::DeclareCommandLineOptions(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")
-      ;
+        ("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")
-      ;
+        ("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 BasicSdlApplication::ExecuteWithSdl(MessageBroker& broker,
-                                          IBasicApplication& application,
-                                        int argc,
-                                        char* argv[])
+  void BasicSdlApplication::Run(BasicNativeApplicationContext& context, const std::string& title, unsigned int width, unsigned int height, bool enableOpenGl)
   {
-    /******************************************************************
-     * Initialize all the subcomponents of Orthanc Stone
-     ******************************************************************/
-
-    Orthanc::Logging::Initialize();
-    Orthanc::Toolbox::InitializeOpenSsl();
-    Orthanc::HttpClient::GlobalInitialize();
-    SdlWindow::GlobalInitialize();
-
-
-    /******************************************************************
-     * Declare and parse the command-line options of the application
-     ******************************************************************/
+    /**************************************************************
+     * Run the application inside a SDL window
+     **************************************************************/
 
-    boost::program_options::options_description options;
-    DeclareSdlCommandLineOptions(options);
-    application.DeclareStartupOptions(options);
-
-    boost::program_options::variables_map parameters;
-    bool error = false;
+    LOG(WARNING) << "Starting the application";
 
-    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;
-    }
+    SdlWindow window(title.c_str(), width, height, enableOpenGl);
+    SdlEngine sdl(window, context);
 
-
-    /******************************************************************
-     * 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";
+      BasicNativeApplicationContext::GlobalMutexLocker locker(context);
+      context.GetCentralViewport().Register(sdl);  // (*)
     }
 
-    bool success = true;
-    try
-    {
-      /****************************************************************
-       * Initialize the connection to the Orthanc server
-       ****************************************************************/
+    context.Start();
+    sdl.Run();
 
-      Orthanc::WebServiceParameters webServiceParameters;
-
-      if (parameters.count("orthanc"))
-      {
-        webServiceParameters.SetUrl(parameters["orthanc"].as<std::string>());
-      }
+    LOG(WARNING) << "Stopping the application";
 
-      if (parameters.count("username"))
-      {
-        webServiceParameters.SetUsername(parameters["username"].as<std::string>());
-      }
-
-      if (parameters.count("password"))
-      {
-        webServiceParameters.SetPassword(parameters["password"].as<std::string>());
-      }
+    // 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();
+  }
 
-      LOG(WARNING) << "URL to the Orthanc REST API: " << webServiceParameters.GetUrl();
-
-      {
-        OrthancPlugins::OrthancHttpConnection orthanc(webServiceParameters);
-        if (!MessagingToolbox::CheckOrthancVersion(orthanc))
-        {
-          LOG(ERROR) << "Your version of Orthanc is incompatible with Stone of Orthanc, please upgrade";
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
-        }
-      }
+  void BasicSdlApplication::Finalize()
+  {
+    SdlWindow::GlobalFinalize();
+  }
 
 
-      /****************************************************************
-       * Initialize the application
-       ****************************************************************/
-
-      LOG(WARNING) << "Creating the widgets of the application";
-
-      LogStatusBar statusBar;
-
-      BasicSdlApplicationContext context;
-      Oracle oracle(4); // use 4 threads to download content
-      OracleWebService webService(broker, oracle, webServiceParameters, context);
-      context.SetWebService(webService);
-
-      application.Initialize(&context, statusBar, parameters);
-
-      {
-        BasicSdlApplicationContext::GlobalMutexLocker locker(context);
-        context.SetCentralWidget(application.GetCentralWidget());
-        context.GetCentralViewport().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);
-
-        {
-          BasicSdlApplicationContext::GlobalMutexLocker locker(context);
-          context.GetCentralViewport().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::Toolbox::FinalizeOpenSsl();
-
-    return (success ? 0 : -1);
-  }
-
 }
--- a/Applications/Sdl/BasicSdlApplication.h	Fri Aug 24 11:26:59 2018 +0200
+++ b/Applications/Sdl/BasicSdlApplication.h	Fri Aug 24 13:52:55 2018 +0200
@@ -21,7 +21,7 @@
 
 #pragma once
 
-#include "../IBasicApplication.h"
+#include "../Generic/BasicNativeApplication.h"
 
 #if ORTHANC_ENABLE_SDL != 1
 #error this file shall be included only with the ORTHANC_ENABLE_SDL set to 1
@@ -31,14 +31,13 @@
 
 namespace OrthancStone
 {
-  class BasicSdlApplication
+  class BasicSdlApplication : public BasicNativeApplication
   {
   public:
-
-    static int ExecuteWithSdl(MessageBroker& broker,
-                              IBasicApplication& application,
-                              int argc,
-                              char* argv[]);
+    virtual void Initialize();
+    virtual void DeclareCommandLineOptions(boost::program_options::options_description& options);
+    virtual void Run(BasicNativeApplicationContext& context, const std::string& title, unsigned int width, unsigned int height, bool enableOpenGl);
+    virtual void Finalize();
   };
 
 }
--- a/Applications/Sdl/BasicSdlApplicationContext.cpp	Fri Aug 24 11:26:59 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,80 +0,0 @@
-/**
- * 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"
-#include "../../Platforms/Generic/OracleWebService.h"
-
-namespace OrthancStone
-{
-  IWidget& BasicSdlApplicationContext::SetCentralWidget(IWidget* widget)   // Takes ownership
-  {
-    centralViewport_->SetCentralWidget(widget);
-    return *widget;
-  }
-
-
-  void BasicSdlApplicationContext::UpdateThread(BasicSdlApplicationContext* that)
-  {
-    while (!that->stopped_)
-    {
-      {
-        GlobalMutexLocker locker(*that);
-        that->GetCentralViewport().UpdateContent();
-      }
-      
-      boost::this_thread::sleep(boost::posix_time::milliseconds(that->updateDelay_));
-    }
-  }
-  
-
-  BasicSdlApplicationContext::BasicSdlApplicationContext() : // Orthanc::WebServiceParameters& orthanc, WidgetViewport* centralViewport) :
-    centralViewport_(new OrthancStone::WidgetViewport()),
-    stopped_(true),
-    updateDelay_(100)   // By default, 100ms between each refresh of the content
-  {
-    srand(time(NULL)); 
-  }
-
-
-  void BasicSdlApplicationContext::Start()
-  {
-    dynamic_cast<OracleWebService*>(webService_)->Start();
-
-    if (centralViewport_->HasUpdateContent())
-    {
-      stopped_ = false;
-      updateThread_ = boost::thread(UpdateThread, this);
-    }
-  }
-
-
-  void BasicSdlApplicationContext::Stop()
-  {
-    stopped_ = true;
-    
-    if (updateThread_.joinable())
-    {
-      updateThread_.join();
-    }
-    
-    dynamic_cast<OracleWebService*>(webService_)->Stop();
-  }
-}
--- a/Applications/Sdl/BasicSdlApplicationContext.h	Fri Aug 24 11:26:59 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,92 +0,0 @@
-/**
- * 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 <list>
-#include <boost/thread.hpp>
-#include "../BasicApplicationContext.h"
-
-namespace OrthancStone
-{
-  class BasicSdlApplicationContext : public BasicApplicationContext
-  {
-  private:
-
-    static void UpdateThread(BasicSdlApplicationContext* that);
-
-    boost::mutex        globalMutex_;
-    std::unique_ptr<WidgetViewport>      centralViewport_;
-    boost::thread       updateThread_;
-    bool                stopped_;
-    unsigned int        updateDelay_;
-
-  public:
-    class GlobalMutexLocker: public boost::noncopyable
-    {
-      boost::mutex::scoped_lock  lock_;
-    public:
-      GlobalMutexLocker(BasicSdlApplicationContext& that):
-        lock_(that.globalMutex_)
-      {}
-    };
-
-//    class ViewportLocker : public boost::noncopyable
-//    {
-//    private:
-//      boost::mutex::scoped_lock  lock_;
-//      IViewport&                 viewport_;
-
-//    public:
-//      ViewportLocker(BasicSdlApplicationContext& that) :
-//        lock_(that.viewportMutex_),
-//        viewport_(*(that.centralViewport_.get()))
-//      {
-//      }
-
-//      IViewport& GetViewport() const
-//      {
-//        return viewport_;
-//      }
-//    };
-
-    
-    BasicSdlApplicationContext();
-
-    virtual ~BasicSdlApplicationContext() {}
-
-    virtual IWidget& SetCentralWidget(IWidget* widget);   // Takes ownership
-    IViewport& GetCentralViewport() {return *(centralViewport_.get());}
-
-    void Start();
-
-    void Stop();
-
-    void SetUpdateDelay(unsigned int delay)  // In milliseconds
-    {
-      updateDelay_ = delay;
-    }
-  };
-}
--- a/Applications/Sdl/SdlEngine.cpp	Fri Aug 24 11:26:59 2018 +0200
+++ b/Applications/Sdl/SdlEngine.cpp	Fri Aug 24 13:52:55 2018 +0200
@@ -41,7 +41,7 @@
   {
     if (viewportChanged_)
     {
-      BasicSdlApplicationContext::GlobalMutexLocker locker(context_);
+      BasicNativeApplicationContext::GlobalMutexLocker locker(context_);
       surface_.Render(context_.GetCentralViewport());
 
       viewportChanged_ = false;
@@ -98,7 +98,7 @@
 
 
   SdlEngine::SdlEngine(SdlWindow& window,
-                       BasicSdlApplicationContext& context) :
+                       BasicNativeApplicationContext& context) :
     window_(window),
     context_(context),
     surface_(window),
@@ -118,7 +118,7 @@
     const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
 
     {
-      BasicSdlApplicationContext::GlobalMutexLocker locker(context_);
+      BasicNativeApplicationContext::GlobalMutexLocker locker(context_);
       SetSize(window_.GetWidth(), window_.GetHeight());
       context_.GetCentralViewport().SetDefaultView();
     }
@@ -133,7 +133,7 @@
       while (!stop &&
              SDL_PollEvent(&event))
       {
-        BasicSdlApplicationContext::GlobalMutexLocker locker(context_);
+        BasicNativeApplicationContext::GlobalMutexLocker locker(context_);
 
         if (event.type == SDL_QUIT) 
         {
--- a/Applications/Sdl/SdlEngine.h	Fri Aug 24 11:26:59 2018 +0200
+++ b/Applications/Sdl/SdlEngine.h	Fri Aug 24 13:52:55 2018 +0200
@@ -24,7 +24,7 @@
 #if ORTHANC_ENABLE_SDL == 1
 
 #include "SdlCairoSurface.h"
-#include "BasicSdlApplicationContext.h"
+#include "../Generic/BasicNativeApplicationContext.h"
 
 namespace OrthancStone
 {
@@ -32,7 +32,7 @@
   {
   private:
     SdlWindow&                window_;
-    BasicSdlApplicationContext&  context_;
+    BasicNativeApplicationContext&  context_;
     SdlCairoSurface           surface_;
     bool                      viewportChanged_;
 
@@ -46,7 +46,7 @@
 
   public:
     SdlEngine(SdlWindow& window,
-              BasicSdlApplicationContext& context);
+              BasicNativeApplicationContext& context);
   
     virtual ~SdlEngine();
 
--- a/Platforms/Generic/OracleWebService.h	Fri Aug 24 11:26:59 2018 +0200
+++ b/Platforms/Generic/OracleWebService.h	Fri Aug 24 13:52:55 2018 +0200
@@ -25,7 +25,7 @@
 #include "Oracle.h"
 #include "WebServiceGetCommand.h"
 #include "WebServicePostCommand.h"
-#include "../../Applications/Sdl/BasicSdlApplicationContext.h"
+#include "../../Applications/Generic/BasicNativeApplicationContext.h"
 
 namespace OrthancStone
 {
@@ -33,14 +33,14 @@
   {
   private:
     Oracle&                        oracle_;
-    BasicSdlApplicationContext&    context_;
+    BasicNativeApplicationContext& context_;
     Orthanc::WebServiceParameters  parameters_;
 
   public:
     OracleWebService(MessageBroker& broker,
                      Oracle& oracle,
                      const Orthanc::WebServiceParameters& parameters,
-                     BasicSdlApplicationContext& context) :
+                     BasicNativeApplicationContext& context) :
       IWebService(broker),
       oracle_(oracle),
       context_(context),
--- a/Platforms/Generic/WebServiceCommandBase.cpp	Fri Aug 24 11:26:59 2018 +0200
+++ b/Platforms/Generic/WebServiceCommandBase.cpp	Fri Aug 24 13:52:55 2018 +0200
@@ -31,7 +31,7 @@
                                                const std::string& uri,
                                                const IWebService::Headers& headers,
                                                Orthanc::IDynamicObject* payload /* takes ownership */,
-                                               BasicSdlApplicationContext& context) :
+                                               BasicNativeApplicationContext& context) :
     IObservable(broker),
     callback_(callback),
     parameters_(parameters),
@@ -48,7 +48,7 @@
 
   void WebServiceCommandBase::Commit()
   {
-    BasicSdlApplicationContext::GlobalMutexLocker lock(context_);  // we want to make sure that, i.e, the UpdateThread is not triggered while we are updating the "model" with the result of a WebServiceCommand
+    BasicNativeApplicationContext::GlobalMutexLocker lock(context_);  // we want to make sure that, i.e, the UpdateThread is not triggered while we are updating the "model" with the result of a WebServiceCommand
 
     if (success_)
     {
--- a/Platforms/Generic/WebServiceCommandBase.h	Fri Aug 24 11:26:59 2018 +0200
+++ b/Platforms/Generic/WebServiceCommandBase.h	Fri Aug 24 13:52:55 2018 +0200
@@ -25,7 +25,7 @@
 
 #include "../../Framework/Toolbox/IWebService.h"
 #include "../../Framework/Messages/IObservable.h"
-#include "../../Applications/Sdl/BasicSdlApplicationContext.h"
+#include "../../Applications/Generic/BasicNativeApplicationContext.h"
 
 #include <Core/WebServiceParameters.h>
 
@@ -43,7 +43,7 @@
     std::auto_ptr<Orthanc::IDynamicObject>  payload_;
     bool                                    success_;
     std::string                             answer_;
-    BasicSdlApplicationContext&             context_;
+    BasicNativeApplicationContext&          context_;
 
   public:
     WebServiceCommandBase(MessageBroker& broker,
@@ -52,7 +52,7 @@
                           const std::string& uri,
                           const std::map<std::string, std::string>& headers,
                           Orthanc::IDynamicObject* payload /* takes ownership */,
-                          BasicSdlApplicationContext& context);
+                          BasicNativeApplicationContext& context);
 
     virtual void Execute() = 0;
 
--- a/Platforms/Generic/WebServiceGetCommand.cpp	Fri Aug 24 11:26:59 2018 +0200
+++ b/Platforms/Generic/WebServiceGetCommand.cpp	Fri Aug 24 13:52:55 2018 +0200
@@ -31,7 +31,7 @@
                                              const std::string& uri,
                                              const IWebService::Headers& headers,
                                              Orthanc::IDynamicObject* payload /* takes ownership */,
-                                             BasicSdlApplicationContext& context) :
+                                             BasicNativeApplicationContext& context) :
     WebServiceCommandBase(broker, callback, parameters, uri, headers, payload, context)
   {
   }
--- a/Platforms/Generic/WebServiceGetCommand.h	Fri Aug 24 11:26:59 2018 +0200
+++ b/Platforms/Generic/WebServiceGetCommand.h	Fri Aug 24 13:52:55 2018 +0200
@@ -34,7 +34,7 @@
                          const std::string& uri,
                          const IWebService::Headers& headers,
                          Orthanc::IDynamicObject* payload /* takes ownership */,
-                         BasicSdlApplicationContext& context);
+                         BasicNativeApplicationContext& context);
 
     virtual void Execute();
   };
--- a/Platforms/Generic/WebServicePostCommand.cpp	Fri Aug 24 11:26:59 2018 +0200
+++ b/Platforms/Generic/WebServicePostCommand.cpp	Fri Aug 24 13:52:55 2018 +0200
@@ -32,7 +32,7 @@
                                                const IWebService::Headers& headers,
                                                const std::string& body,
                                                Orthanc::IDynamicObject* payload /* takes ownership */,
-                                               BasicSdlApplicationContext& context) :
+                                               BasicNativeApplicationContext& context) :
     WebServiceCommandBase(broker, callback, parameters, uri, headers, payload, context),
     body_(body)
   {
--- a/Platforms/Generic/WebServicePostCommand.h	Fri Aug 24 11:26:59 2018 +0200
+++ b/Platforms/Generic/WebServicePostCommand.h	Fri Aug 24 13:52:55 2018 +0200
@@ -38,7 +38,7 @@
                           const IWebService::Headers& headers,
                           const std::string& body,
                           Orthanc::IDynamicObject* payload /* takes ownership */,
-                          BasicSdlApplicationContext& context);
+                          BasicNativeApplicationContext& context);
 
     virtual void Execute();
   };
--- a/Platforms/Wasm/CMakeLists.txt	Fri Aug 24 11:26:59 2018 +0200
+++ b/Platforms/Wasm/CMakeLists.txt	Fri Aug 24 13:52:55 2018 +0200
@@ -31,7 +31,9 @@
 include(../../Resources/CMake/OrthancStoneParameters.cmake)
 
 SET(ORTHANC_SANDBOXED ON)
+SET(ENABLE_QT OFF)
 SET(ENABLE_SDL OFF)
+add_definitions(-DORTHANC_ENABLE_WASM=1)
 
 include(../../Resources/CMake/OrthancStoneConfiguration.cmake)
 
--- a/Resources/CMake/OrthancStoneConfiguration.cmake	Fri Aug 24 11:26:59 2018 +0200
+++ b/Resources/CMake/OrthancStoneConfiguration.cmake	Fri Aug 24 13:52:55 2018 +0200
@@ -39,6 +39,10 @@
     message(FATAL_ERROR "Cannot enable SDL in sandboxed environments")
   endif()
 
+  if (ENABLE_QT)
+    message(FATAL_ERROR "Cannot enable QT in sandboxed environments")
+  endif()
+
   if (ENABLE_SSL)
     message(FATAL_ERROR "Cannot enable SSL in sandboxed environments")
   endif()
@@ -69,12 +73,21 @@
 endif()
 
 
-if (ENABLE_SDL)
+if (ENABLE_SDL AND ENABLE_QT)
+    message("SDL and QT may not be defined together")
+elseif(ENABLE_SDL)
   include(${CMAKE_CURRENT_LIST_DIR}/SdlConfiguration.cmake)  
+  add_definitions(-DORTHANC_ENABLE_NATIVE=1)
   add_definitions(-DORTHANC_ENABLE_SDL=1)
+elseif(ENABLE_QT)
+    include(${CMAKE_CURRENT_LIST_DIR}/QtConfiguration.cmake)
+    add_definitions(-DORTHANC_ENABLE_NATIVE=1)
+    add_definitions(-DORTHANC_ENABLE_QT=1)
 else()
   unset(USE_SYSTEM_SDL CACHE)
   add_definitions(-DORTHANC_ENABLE_SDL=0)
+  add_definitions(-DORTHANC_ENABLE_QT=0)
+  add_definitions(-DORTHANC_ENABLE_NATIVE=0)
 endif()
 
 
@@ -157,14 +170,21 @@
     ${ORTHANC_STONE_ROOT}/Platforms/Generic/OracleWebService.h
     )
 
-  list(APPEND APPLICATIONS_SOURCES
-    ${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
-    ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlWindow.cpp
-    )
+  if (ENABLE_SDL OR ENABLE_QT)
+    list(APPEND APPLICATIONS_SOURCES
+      ${ORTHANC_STONE_ROOT}/Applications/Generic/BasicNativeApplication.cpp
+      ${ORTHANC_STONE_ROOT}/Applications/Generic/BasicNativeApplicationContext.cpp
+      )
+    if (ENABLE_SDL)
+      list(APPEND APPLICATIONS_SOURCES
+        ${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
+        )
+    endif()
+  endif()
 else()
   list(APPEND APPLICATIONS_SOURCES
     ${ORTHANC_STONE_ROOT}/Applications/Wasm/StartupParametersBuilder.cpp
--- a/Resources/CMake/OrthancStoneParameters.cmake	Fri Aug 24 11:26:59 2018 +0200
+++ b/Resources/CMake/OrthancStoneParameters.cmake	Fri Aug 24 13:52:55 2018 +0200
@@ -50,3 +50,4 @@
 #####################################################################
 
 set(ENABLE_SDL ON CACHE INTERNAL "Include support for SDL")
+set(ENABLE_QT OFF CACHE INTERNAL "Include support for Qt")