changeset 1062:2d6237221ff1

Merge
author Benjamin Golinvaux <bgo@osimis.io>
date Tue, 15 Oct 2019 10:54:53 +0200
parents eb28dfe432f7 (current diff) 5df50e0f0390 (diff)
children 3e8f59cefec6 6dd90b8d1589
files Applications/Sdl/SdlOpenGLContext.cpp Applications/Sdl/SdlOpenGLContext.h Applications/Sdl/SdlWindow.cpp Applications/Sdl/SdlWindow.h Framework/Messages/Promise.h UnitTestsSources/TestExceptions.cpp
diffstat 57 files changed, 931 insertions(+), 1285 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/Samples/CMakeLists.txt	Tue Oct 15 10:53:28 2019 +0200
+++ b/Applications/Samples/CMakeLists.txt	Tue Oct 15 10:54:53 2019 +0200
@@ -232,7 +232,6 @@
   add_executable(UnitTests
     ${GOOGLE_TEST_SOURCES}
     ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestCommands.cpp
-    ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestExceptions.cpp
     ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestMessageBroker.cpp
     ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestStrategy.cpp
     ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestStructureSet.cpp
--- a/Applications/Samples/SingleFrameEditorApplication.h	Tue Oct 15 10:53:28 2019 +0200
+++ b/Applications/Samples/SingleFrameEditorApplication.h	Tue Oct 15 10:54:53 2019 +0200
@@ -117,12 +117,12 @@
           if (tool_ == Tool_Windowing)
           {
             return new RadiographyWindowingTracker(
-                  undoRedoStack_, widget.GetScene(),
-                  viewportX, viewportY,
-                  RadiographyWindowingTracker::Action_DecreaseWidth,
-                  RadiographyWindowingTracker::Action_IncreaseWidth,
-                  RadiographyWindowingTracker::Action_DecreaseCenter,
-                  RadiographyWindowingTracker::Action_IncreaseCenter);
+              undoRedoStack_, widget.GetScene(), widget, ImageInterpolation_Nearest,
+              viewportX, viewportY,
+              RadiographyWindowingTracker::Action_DecreaseWidth,
+              RadiographyWindowingTracker::Action_IncreaseWidth,
+              RadiographyWindowingTracker::Action_DecreaseCenter,
+              RadiographyWindowingTracker::Action_IncreaseCenter);
           }
           else if (!widget.LookupSelectedLayer(selected))
           {
--- a/Applications/Sdl/SdlCairoSurface.h	Tue Oct 15 10:53:28 2019 +0200
+++ b/Applications/Sdl/SdlCairoSurface.h	Tue Oct 15 10:54:53 2019 +0200
@@ -23,7 +23,7 @@
 
 #if ORTHANC_ENABLE_SDL == 1
 
-#include "SdlWindow.h"
+#include "../../Framework/Viewport/SdlWindow.h"
 #include "../../Framework/Wrappers/CairoSurface.h"
 #include "../../Framework/Deprecated/Viewport/IViewport.h"
 
--- a/Applications/Sdl/SdlOpenGLContext.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,162 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 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 "SdlOpenGLContext.h"
-#include "../../Framework/StoneException.h"
-
-#if ORTHANC_ENABLE_SDL == 1
-
-#if !defined(ORTHANC_ENABLE_GLEW)
-#  error Macro ORTHANC_ENABLE_GLEW must be defined
-#endif
-
-#if ORTHANC_ENABLE_GLEW == 1
-#  include <GL/glew.h>
-#endif
-
-#include <Core/OrthancException.h>
-
-namespace OrthancStone
-{
-  SdlOpenGLContext::SdlOpenGLContext(const char* title,
-                                     unsigned int width,
-                                     unsigned int height,
-                                     bool allowDpiScaling) 
-    : window_(title, width, height, true /* enable OpenGL */, allowDpiScaling)
-    , context_(NULL)
-    , contextLost_(false)
-  {
-    context_ = SDL_GL_CreateContext(window_.GetObject());
-    
-    if (context_ == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
-                                      "Cannot initialize OpenGL");
-    }
-
-#if ORTHANC_ENABLE_GLEW == 1
-    // The initialization function of glew (i.e. "glewInit()") can
-    // only be called once an OpenGL is setup.
-    // https://stackoverflow.com/a/45033669/881731
-    {
-      static boost::mutex  mutex_;
-      static bool          isGlewInitialized_ = false;
-  
-      boost::mutex::scoped_lock lock(mutex_);
-
-      if (!isGlewInitialized_)
-      {
-        LOG(INFO) << "Initializing glew";
-        
-        GLenum err = glewInit();
-        if (GLEW_OK != err)
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
-                                          "Cannot initialize glew");
-        }
-
-        isGlewInitialized_ = true;
-      }
-    }    
-#endif
-  }
-
-  
-  SdlOpenGLContext::~SdlOpenGLContext()
-  {
-    SDL_GL_DeleteContext(context_);
-  }
-
-
-  bool SdlOpenGLContext::IsContextLost()
-  {
-    return contextLost_;
-  }
-  
-  void SdlOpenGLContext::SetLostContext()
-  {
-    contextLost_ = true;
-  }
-
-  void SdlOpenGLContext::RestoreLostContext()
-  {
-    contextLost_ = false;
-  }
-   
-  // extern bool Debug_MustContextBeKilled(std::string title);
-  // extern void Debug_Context_ClearKillFlag(std::string title);
-
-  void SdlOpenGLContext::MakeCurrent()
-  {
-    if (IsContextLost())
-      throw OpenGLContextLostException(context_);
-
-    // <DEBUG STUFF>
-    // This is used for context loss simulation
-    // SDL_Window* internalWindow = GetWindow().GetObject();
-    // std::string title(SDL_GetWindowTitle(internalWindow));
-
-    // if (Debug_MustContextBeKilled(title))
-    // {
-    //   Debug_Context_ClearKillFlag(title);
-    //   SetLostContext();
-    //   throw OpenGLContextLostException(context_);
-    // }
-    // </DEBUG STUFF>
-
-    if (SDL_GL_MakeCurrent(window_.GetObject(), context_) != 0)
-    {
-      const char* errText = SDL_GetError();
-      std::stringstream ss;
-      ss << "Cannot set current OpenGL context. SDL error text: " << errText;
-      std::string errStr = ss.str();
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, errStr.c_str());
-    }
-
-    // This makes our buffer swap synchronized with the monitor's vertical refresh
-    SDL_GL_SetSwapInterval(1);
-  }
-
-  
-  void SdlOpenGLContext::SwapBuffer()
-  {
-    // Swap our buffer to display the current contents of buffer on screen
-    SDL_GL_SwapWindow(window_.GetObject());
-  }
-
-  
-  unsigned int SdlOpenGLContext::GetCanvasWidth() const
-  {
-    int w = 0;
-    SDL_GL_GetDrawableSize(window_.GetObject(), &w, NULL);
-    return static_cast<unsigned int>(w);
-  }
-
-  
-  unsigned int SdlOpenGLContext::GetCanvasHeight() const
-  {
-    int h = 0;
-    SDL_GL_GetDrawableSize(window_.GetObject(), NULL, &h);
-    return static_cast<unsigned int>(h);
-  }
-}
-
-#endif
--- a/Applications/Sdl/SdlOpenGLContext.h	Tue Oct 15 10:53:28 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 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
-
-#if ORTHANC_ENABLE_SDL == 1
-
-#include "../../Framework/OpenGL/IOpenGLContext.h"
-#include "SdlWindow.h"
-
-#include <Core/Enumerations.h>
-
-namespace OrthancStone
-{
-  class SdlOpenGLContext : public OpenGL::IOpenGLContext
-  {
-  private:
-    SdlWindow      window_;
-    SDL_GLContext  context_;
-    bool           contextLost_;
-
-  public:
-    SdlOpenGLContext(const char* title,
-                     unsigned int width,
-                     unsigned int height,
-                     bool allowDpiScaling = true);
-
-    ~SdlOpenGLContext();
-
-    SdlWindow& GetWindow()
-    {
-      return window_;
-    }
-
-    virtual bool IsContextLost() ORTHANC_OVERRIDE;
-
-    virtual void SetLostContext() ORTHANC_OVERRIDE;
-    virtual void RestoreLostContext() ORTHANC_OVERRIDE;
-
-    virtual void MakeCurrent() ORTHANC_OVERRIDE;
-
-    virtual void SwapBuffer() ORTHANC_OVERRIDE;
-
-    virtual unsigned int GetCanvasWidth() const ORTHANC_OVERRIDE;
-
-    virtual unsigned int GetCanvasHeight() const ORTHANC_OVERRIDE;
-
-    virtual void* DebugGetInternalContext() const ORTHANC_OVERRIDE
-    {
-      return context_;
-    }
-  };
-}
-
-#endif
--- a/Applications/Sdl/SdlOrthancSurface.h	Tue Oct 15 10:53:28 2019 +0200
+++ b/Applications/Sdl/SdlOrthancSurface.h	Tue Oct 15 10:54:53 2019 +0200
@@ -23,7 +23,7 @@
 
 #if ORTHANC_ENABLE_SDL == 1
 
-#include "SdlWindow.h"
+#include "../../Framework/Viewport/SdlWindow.h"
 
 #include <Core/Images/ImageAccessor.h>
 #include <boost/thread/mutex.hpp>
--- a/Applications/Sdl/SdlWindow.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,201 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 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 "SdlWindow.h"
-
-#if ORTHANC_ENABLE_SDL == 1
-
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-
-#ifdef WIN32 
-#include <windows.h> // for SetProcessDpiAware
-#endif 
-// WIN32
-
-#include <SDL.h>
-
-namespace OrthancStone
-{
-  SdlWindow::SdlWindow(const char* title,
-                       unsigned int width,
-                       unsigned int height,
-                       bool enableOpenGl,
-                       bool allowDpiScaling) :
-    maximized_(false)
-  {
-    // TODO Understand why, with SDL_WINDOW_OPENGL + MinGW32 + Release
-    // build mode, the application crashes whenever the SDL window is
-    // resized or maximized
-
-    uint32_t windowFlags, rendererFlags;
-    if (enableOpenGl)
-    {
-      windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE;
-      rendererFlags = SDL_RENDERER_ACCELERATED;
-    }
-    else
-    {
-      windowFlags = SDL_WINDOW_RESIZABLE;
-      rendererFlags = SDL_RENDERER_SOFTWARE;
-    }
-
-// TODO: probably required on MacOS X, too
-#if defined(WIN32) && (_WIN32_WINNT >= 0x0600)
-    if (!allowDpiScaling)
-    {
-      // if we do NOT allow DPI scaling, it means an SDL pixel will be a real
-      // monitor pixel. This is needed for high-DPI applications
-
-      // Enable high-DPI support on Windows
-
-      // THE FOLLOWING HAS BEEN COMMENTED OUT BECAUSE IT WILL CRASH UNDER 
-      // OLD WINDOWS VERSIONS
-      // ADD THIS AT THE TOP TO ENABLE IT:
-      // 
-      //#pragma comment(lib, "Shcore.lib") THIS IS ONLY REQUIRED FOR SetProcessDpiAwareness
-      //#include <windows.h>
-      //#include <ShellScalingAPI.h> THIS IS ONLY REQUIRED FOR SetProcessDpiAwareness
-      //#include <comdef.h> THIS IS ONLY REQUIRED FOR SetProcessDpiAwareness
-      // SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
-      
-      // This is supported on Vista+
-      SetProcessDPIAware();
-
-      windowFlags |= SDL_WINDOW_ALLOW_HIGHDPI;
-    }
-#endif 
-// WIN32
-    
-    window_ = SDL_CreateWindow(title,
-                               SDL_WINDOWPOS_UNDEFINED,
-                               SDL_WINDOWPOS_UNDEFINED,
-                               width, height, windowFlags);
-
-    if (window_ == NULL) 
-    {
-      LOG(ERROR) << "Cannot create the SDL window: " << SDL_GetError();
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-    }
-
-    renderer_ = SDL_CreateRenderer(window_, -1, rendererFlags);
-    if (!renderer_)
-    {
-      LOG(ERROR) << "Cannot create the SDL renderer: " << SDL_GetError();
-      SDL_DestroyWindow(window_);
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-    }
-  }
-
-
-  SdlWindow::~SdlWindow()
-  {
-    if (renderer_ != NULL)
-    { 
-      SDL_DestroyRenderer(renderer_);
-    }
-
-    if (window_ != NULL)
-    { 
-      SDL_DestroyWindow(window_);
-    }
-  }
-
-
-  unsigned int SdlWindow::GetWidth() const
-  {
-    int w = -1;
-    SDL_GetWindowSize(window_, &w, NULL);
-
-    if (w < 0)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-    }
-    else
-    {
-      return static_cast<unsigned int>(w);
-    }
-  }
-
-
-  unsigned int SdlWindow::GetHeight() const
-  {
-    int h = -1;
-    SDL_GetWindowSize(window_, NULL, &h);
-
-    if (h < 0)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-    }
-    else
-    {
-      return static_cast<unsigned int>(h);
-    }
-  }
-
-
-  void SdlWindow::Render(SDL_Surface* surface)
-  {
-    //SDL_RenderClear(renderer_);
-
-    SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer_, surface);
-    if (texture != NULL)
-    {
-      SDL_RenderCopy(renderer_, texture, NULL, NULL);
-      SDL_DestroyTexture(texture);
-    }
-
-    SDL_RenderPresent(renderer_);
-  }
-
-
-  void SdlWindow::ToggleMaximize()
-  {
-    if (maximized_)
-    {
-      SDL_RestoreWindow(window_);
-      maximized_ = false;
-    }
-    else
-    {
-      SDL_MaximizeWindow(window_);
-      maximized_ = true;
-    }
-  }
-
-
-  void SdlWindow::GlobalInitialize()
-  {
-    if (SDL_Init(SDL_INIT_VIDEO) != 0)
-    {
-      LOG(ERROR) << "Cannot initialize SDL";
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-    }
-  }
-
-
-  void SdlWindow::GlobalFinalize()
-  {
-    SDL_Quit();
-  }
-}
-
-#endif
--- a/Applications/Sdl/SdlWindow.h	Tue Oct 15 10:53:28 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 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
-
-#if ORTHANC_ENABLE_SDL == 1
-
-#include <SDL_render.h>
-#include <SDL_video.h>
-#include <boost/noncopyable.hpp>
-
-namespace OrthancStone
-{
-  class SdlWindow : public boost::noncopyable
-  {
-  private:
-    SDL_Window    *window_;
-    SDL_Renderer  *renderer_;
-    bool           maximized_;
-
-  public:
-    SdlWindow(const char* title,
-              unsigned int width,
-              unsigned int height,
-              bool enableOpenGl,
-              bool allowDpiScaling = true);
-
-    ~SdlWindow();
-
-    SDL_Window *GetObject() const
-    {
-      return window_;
-    }
-
-    unsigned int GetWidth() const;
-
-    unsigned int GetHeight() const;
-
-    void Render(SDL_Surface* surface);
-
-    void ToggleMaximize();
-
-    static void GlobalInitialize();
-
-    static void GlobalFinalize();
-  };
-}
-
-#endif
--- a/Framework/Deprecated/Toolbox/IWebService.h	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Deprecated/Toolbox/IWebService.h	Tue Oct 15 10:54:53 2019 +0200
@@ -24,6 +24,7 @@
 #include "../../Messages/IObserver.h"
 #include "../../Messages/ICallable.h"
 
+#include <Core/Enumerations.h>
 #include <Core/IDynamicObject.h>
 #include <Core/Logging.h>
 
--- a/Framework/Deprecated/Toolbox/OrthancApiClient.h	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Deprecated/Toolbox/OrthancApiClient.h	Tue Oct 15 10:54:53 2019 +0200
@@ -26,7 +26,6 @@
 
 #include "IWebService.h"
 #include "../../Messages/IObservable.h"
-#include "../../Messages/Promise.h"
 
 namespace Deprecated
 {
--- a/Framework/Loaders/DicomStructureSetLoader.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Loaders/DicomStructureSetLoader.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -22,8 +22,11 @@
 #include "DicomStructureSetLoader.h"
 
 #include "../Scene2D/PolylineSceneLayer.h"
+#include "../StoneException.h"
 #include "../Toolbox/GeometryToolbox.h"
 
+#include <Core/Toolbox.h>
+
 #include <algorithm>
 
 #if 0
--- a/Framework/Loaders/LoaderCache.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Loaders/LoaderCache.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -20,6 +20,7 @@
 
 #include "LoaderCache.h"
 
+#include "../StoneException.h"
 #include "OrthancSeriesVolumeProgressiveLoader.h"
 #include "OrthancMultiframeVolumeLoader.h"
 #include "DicomStructureSetLoader.h"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Messages/IObserver.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -0,0 +1,93 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 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 "IObserver.h"
+
+#include "IMessage.h"
+#include "../StoneException.h"
+
+#include <Core/Logging.h>
+#include <Core/Toolbox.h>
+
+namespace OrthancStone 
+{
+  IObserver::IObserver(MessageBroker& broker)
+    : broker_(broker)
+    , fingerprint_()
+  {
+    // we store the fingerprint_ as a char array to avoid problems when
+    // reading it in a deceased object.
+    // remember this is panic-level code to track zombie object usage
+    std::string fingerprint = Orthanc::Toolbox::GenerateUuid();
+    const char* fingerprintRaw = fingerprint.c_str();
+    memcpy(fingerprint_, fingerprintRaw, 37);
+    broker_.Register(*this);
+  }
+
+
+  IObserver::~IObserver()
+  {
+    try
+    {
+      LOG(TRACE) << "IObserver(" << std::hex << this << std::dec << ")::~IObserver : fingerprint_ == " << fingerprint_;
+      const char* deadMarker = "deadbeef-dead-dead-0000-0000deadbeef";
+      ORTHANC_ASSERT(strlen(deadMarker) == 36);
+      memcpy(fingerprint_, deadMarker, 37);
+      broker_.Unregister(*this);
+    }
+    catch (const Orthanc::OrthancException& e)
+    {
+      if (e.HasDetails())
+      {
+        LOG(ERROR) << "OrthancException in ~IObserver: " << e.What() << " Details: " << e.GetDetails();
+      }
+      else
+      {
+        LOG(ERROR) << "OrthancException in ~IObserver: " << e.What();
+      }
+    }
+    catch (const std::exception& e)
+    {
+      LOG(ERROR) << "std::exception in ~IObserver: " << e.what();
+    }
+    catch (...)
+    {
+      LOG(ERROR) << "Unknown exception in ~IObserver";
+    }
+  }
+
+
+  bool IObserver::DoesFingerprintLookGood() const
+  {
+    for (size_t i = 0; i < 36; ++i) {
+      bool ok = false;
+      if (fingerprint_[i] >= 'a' && fingerprint_[i] <= 'f')
+        ok = true;
+      if (fingerprint_[i] >= '0' && fingerprint_[i] <= '9')
+        ok = true;
+      if (fingerprint_[i] == '-')
+        ok = true;
+      if (!ok)
+        return false;
+    }
+    return fingerprint_[36] == 0;
+  }
+}
--- a/Framework/Messages/IObserver.h	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Messages/IObserver.h	Tue Oct 15 10:54:53 2019 +0200
@@ -22,9 +22,6 @@
 #pragma once
 
 #include "MessageBroker.h"
-#include "IMessage.h"
-
-#include <Core/Toolbox.h>
 
 namespace OrthancStone 
 {
@@ -35,71 +32,18 @@
     // the following is a UUID that is used to disambiguate different observers
     // that may have the same address
     char     fingerprint_[37];
-  public:
-    IObserver(MessageBroker& broker)
-      : broker_(broker)
-      , fingerprint_()
-    {
-      // we store the fingerprint_ as a char array to avoid problems when
-      // reading it in a deceased object.
-      // remember this is panic-level code to track zombie object usage
-      std::string fingerprint = Orthanc::Toolbox::GenerateUuid();
-      const char* fingerprintRaw = fingerprint.c_str();
-      memcpy(fingerprint_, fingerprintRaw, 37);
-      broker_.Register(*this);
-    }
 
-    virtual ~IObserver()
-    {
-      try
-      {
-        LOG(TRACE) << "IObserver(" << std::hex << this << std::dec << ")::~IObserver : fingerprint_ == " << fingerprint_;
-        const char* deadMarker = "deadbeef-dead-dead-0000-0000deadbeef";
-        ORTHANC_ASSERT(strlen(deadMarker) == 36);
-        memcpy(fingerprint_, deadMarker, 37);
-        broker_.Unregister(*this);
-      }
-      catch (const Orthanc::OrthancException& e)
-      {
-        if (e.HasDetails())
-        {
-          LOG(ERROR) << "OrthancException in ~IObserver: " << e.What() << " Details: " << e.GetDetails();
-        }
-        else
-        {
-          LOG(ERROR) << "OrthancException in ~IObserver: " << e.What();
-        }
-      }
-      catch (const std::exception& e)
-      {
-        LOG(ERROR) << "std::exception in ~IObserver: " << e.what();
-      }
-      catch (...)
-      {
-        LOG(ERROR) << "Unknown exception in ~IObserver";
-      }
-    }
+  public:
+    IObserver(MessageBroker& broker);
+
+    virtual ~IObserver();
 
     const char* GetFingerprint() const
     {
       return fingerprint_;
     }
 
-    bool DoesFingerprintLookGood() const
-    {
-      for (size_t i = 0; i < 36; ++i) {
-        bool ok = false;
-        if (fingerprint_[i] >= 'a' && fingerprint_[i] <= 'f')
-          ok = true;
-        if (fingerprint_[i] >= '0' && fingerprint_[i] <= '9')
-          ok = true;
-        if (fingerprint_[i] == '-')
-          ok = true;
-        if (!ok)
-          return false;
-      }
-      return fingerprint_[36] == 0;
-    }
+    bool DoesFingerprintLookGood() const;
 
     MessageBroker& GetBroker() const
     {
--- a/Framework/Messages/MessageBroker.h	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Messages/MessageBroker.h	Tue Oct 15 10:54:53 2019 +0200
@@ -20,8 +20,6 @@
 
 #pragma once
 
-#include "../StoneException.h"
-
 #include "boost/noncopyable.hpp"
 
 #include <set>
--- a/Framework/Messages/Promise.h	Tue Oct 15 10:53:28 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 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 "MessageBroker.h"
-#include "ICallable.h"
-#include "IMessage.h"
-
-#include <boost/noncopyable.hpp>
-#include <memory>
-
-namespace OrthancStone {
-
-  class Promise : public boost::noncopyable
-  {
-  protected:
-    MessageBroker&                    broker_;
-
-    std::auto_ptr<ICallable> successCallable_;
-    std::auto_ptr<ICallable> failureCallable_;
-
-  public:
-    Promise(MessageBroker& broker)
-      : broker_(broker)
-    {
-    }
-
-    void Success(const IMessage& message)
-    {
-      // check the target is still alive in the broker
-      if (successCallable_.get() != NULL &&
-          broker_.IsActive(*successCallable_->GetObserver()))
-      {
-        successCallable_->Apply(message);
-      }
-    }
-
-    void Failure(const IMessage& message)
-    {
-      // check the target is still alive in the broker
-      if (failureCallable_.get() != NULL &&
-          broker_.IsActive(*failureCallable_->GetObserver()))
-      {
-        failureCallable_->Apply(message);
-      }
-    }
-
-    Promise& Then(ICallable* successCallable)   // Takes ownership
-    {
-      if (successCallable_.get() != NULL)
-      {
-        // TODO: throw throw new "Promise may only have a single success target"
-      }
-      successCallable_.reset(successCallable);
-      return *this;
-    }
-
-    Promise& Else(ICallable* failureCallable)   // Takes ownership
-    {
-      if (failureCallable_.get() != NULL)
-      {
-        // TODO: throw throw new "Promise may only have a single failure target"
-      }
-      failureCallable_.reset(failureCallable);
-      return *this;
-    }
-  };
-}
--- a/Framework/OpenGL/IOpenGLContext.h	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/OpenGL/IOpenGLContext.h	Tue Oct 15 10:54:53 2019 +0200
@@ -34,7 +34,7 @@
       {
       }
 
-      virtual bool IsContextLost() = 0;
+      virtual bool IsContextLost() = 0;
 
       virtual void MakeCurrent() = 0;
 
@@ -43,14 +43,6 @@
       virtual unsigned int GetCanvasWidth() const = 0;
 
       virtual unsigned int GetCanvasHeight() const = 0;
-
-      virtual void* DebugGetInternalContext() const = 0;
-
-      // This is for manual context loss (debug purposes)
-      virtual void SetLostContext() = 0;
-      virtual void RestoreLostContext() = 0;
-
     };
   }
 }
-
--- a/Framework/OpenGL/OpenGLIncludes.h	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/OpenGL/OpenGLIncludes.h	Tue Oct 15 10:54:53 2019 +0200
@@ -58,7 +58,6 @@
 
 #   define ORTHANC_OPENGL_CHECK(name)
 #   define ORTHANC_OPENGL_TRACE_CURRENT_CONTEXT(msg)
-#   define ORTHANC_CHECK_CURRENT_CONTEXT(context)
 
 # else 
 
@@ -80,17 +79,6 @@
   LOG(TRACE) << msg << " | Current OpenGL context is " << std::hex << ctx; \
 } else (void)0
 
-#   define ORTHANC_CHECK_CURRENT_CONTEXT(context)                                                                                                         \
-if(true)                                                                                                                                               \
-{                                                                                                                                                      \
-  SDL_GLContext actualCtx = SDL_GL_GetCurrentContext();                                                                                                \
-  void* expectedCtx = context.DebugGetInternalContext();                                                                                               \
-  if(expectedCtx != actualCtx)                                                                                                                         \
-  {                                                                                                                                                    \
-    LOG(ERROR) << "Expected context was " << std::hex << expectedCtx << " while actual context is " << std::hex << actualCtx;                          \
-  }                                                                                                                                                    \
-} else (void)0
-
 # endif
 
 #endif
--- a/Framework/OpenGL/OpenGLProgram.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/OpenGL/OpenGLProgram.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -49,7 +49,6 @@
       {
         if (!context_.IsContextLost())
         {
-          ORTHANC_CHECK_CURRENT_CONTEXT(context_);
           ORTHANC_OPENGL_TRACE_CURRENT_CONTEXT("About to call glDeleteProgram");
           assert(program_ != 0);
           glDeleteProgram(program_);
--- a/Framework/OpenGL/OpenGLTexture.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/OpenGL/OpenGLTexture.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -35,8 +35,6 @@
     {
       if (!context_.IsContextLost())
       {
-        // context is made current externally. Let's check this!
-        ORTHANC_CHECK_CURRENT_CONTEXT(context_);
         // Generate a texture object
         glGenTextures(1, &texture_);
         if (texture_ == 0)
@@ -53,8 +51,6 @@
       {
         if (!context_.IsContextLost())
         {
-          // context is made current externally. Let's check this!
-          ORTHANC_CHECK_CURRENT_CONTEXT(context_);
           assert(texture_ != 0);
           ORTHANC_OPENGL_TRACE_CURRENT_CONTEXT("About to call glDeleteTextures");
           glDeleteTextures(1, &texture_);
@@ -84,8 +80,6 @@
     void OpenGLTexture::Load(const Orthanc::ImageAccessor& image,
                              bool isLinearInterpolation)
     {
-      // context is made current externally. Let's check this!
-      ORTHANC_CHECK_CURRENT_CONTEXT(context_);
       if (!context_.IsContextLost())
       {
         glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // Disable byte-alignment restriction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/OpenGL/SdlOpenGLContext.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -0,0 +1,126 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 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 "SdlOpenGLContext.h"
+#include "../../Framework/StoneException.h"
+
+#if ORTHANC_ENABLE_SDL == 1
+
+#if !defined(ORTHANC_ENABLE_GLEW)
+#  error Macro ORTHANC_ENABLE_GLEW must be defined
+#endif
+
+#if ORTHANC_ENABLE_GLEW == 1
+#  include <GL/glew.h>
+#endif
+
+#include <Core/OrthancException.h>
+
+namespace OrthancStone
+{
+  SdlOpenGLContext::SdlOpenGLContext(const char* title,
+                                     unsigned int width,
+                                     unsigned int height,
+                                     bool allowDpiScaling) 
+    : window_(title, width, height, true /* enable OpenGL */, allowDpiScaling)
+    , context_(NULL)
+  {
+    context_ = SDL_GL_CreateContext(window_.GetObject());
+    
+    if (context_ == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
+                                      "Cannot initialize OpenGL");
+    }
+
+#if ORTHANC_ENABLE_GLEW == 1
+    // The initialization function of glew (i.e. "glewInit()") can
+    // only be called once an OpenGL is setup.
+    // https://stackoverflow.com/a/45033669/881731
+    {
+      static boost::mutex  mutex_;
+      static bool          isGlewInitialized_ = false;
+  
+      boost::mutex::scoped_lock lock(mutex_);
+
+      if (!isGlewInitialized_)
+      {
+        LOG(INFO) << "Initializing glew";
+        
+        GLenum err = glewInit();
+        if (GLEW_OK != err)
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
+                                          "Cannot initialize glew");
+        }
+
+        isGlewInitialized_ = true;
+      }
+    }    
+#endif
+  }
+
+  
+  SdlOpenGLContext::~SdlOpenGLContext()
+  {
+    SDL_GL_DeleteContext(context_);
+  }
+
+  void SdlOpenGLContext::MakeCurrent()
+  {
+    if (SDL_GL_MakeCurrent(window_.GetObject(), context_) != 0)
+    {
+      const char* errText = SDL_GetError();
+      std::stringstream ss;
+      ss << "Cannot set current OpenGL context. SDL error text: " << errText;
+      std::string errStr = ss.str();
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, errStr.c_str());
+    }
+
+    // This makes our buffer swap synchronized with the monitor's vertical refresh
+    SDL_GL_SetSwapInterval(1);
+  }
+
+  
+  void SdlOpenGLContext::SwapBuffer()
+  {
+    // Swap our buffer to display the current contents of buffer on screen
+    SDL_GL_SwapWindow(window_.GetObject());
+  }
+
+  
+  unsigned int SdlOpenGLContext::GetCanvasWidth() const
+  {
+    int w = 0;
+    SDL_GL_GetDrawableSize(window_.GetObject(), &w, NULL);
+    return static_cast<unsigned int>(w);
+  }
+
+  
+  unsigned int SdlOpenGLContext::GetCanvasHeight() const
+  {
+    int h = 0;
+    SDL_GL_GetDrawableSize(window_.GetObject(), NULL, &h);
+    return static_cast<unsigned int>(h);
+  }
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/OpenGL/SdlOpenGLContext.h	Tue Oct 15 10:54:53 2019 +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-2019 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
+
+#if ORTHANC_ENABLE_SDL == 1
+
+#include "IOpenGLContext.h"
+#include "../Viewport/SdlWindow.h"
+
+#include <Core/Enumerations.h>
+
+namespace OrthancStone
+{
+  class SdlOpenGLContext : public OpenGL::IOpenGLContext
+  {
+  private:
+    SdlWindow      window_;
+    SDL_GLContext  context_;
+
+  public:
+    SdlOpenGLContext(const char* title,
+                     unsigned int width,
+                     unsigned int height,
+                     bool allowDpiScaling = true);
+
+    ~SdlOpenGLContext();
+
+    SdlWindow& GetWindow()
+    {
+      return window_;
+    }
+
+    virtual bool IsContextLost() ORTHANC_OVERRIDE
+    {
+      // On desktop applications, an OpenGL context should never be lost
+      return false;
+    }
+
+    virtual void MakeCurrent() ORTHANC_OVERRIDE;
+
+    virtual void SwapBuffer() ORTHANC_OVERRIDE;
+
+    virtual unsigned int GetCanvasWidth() const ORTHANC_OVERRIDE;
+
+    virtual unsigned int GetCanvasHeight() const ORTHANC_OVERRIDE;
+  };
+}
+
+#endif
--- a/Framework/OpenGL/WebAssemblyOpenGLContext.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/OpenGL/WebAssemblyOpenGLContext.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -123,14 +123,14 @@
         if (IsContextLost())
         {
           LOG(ERROR) << "MakeCurrent() called on lost context " << context_;
-          throw OpenGLContextLostException(reinterpret_cast<void*>(context_));
+          throw StoneException(ErrorCode_WebGLContextLost);
         }
 
         if (emscripten_is_webgl_context_lost(context_))
         {
           LOG(ERROR) << "OpenGL context has been lost for canvas: " << canvas_;
           SetLostContext();
-          throw OpenGLContextLostException(reinterpret_cast<void*>(context_));
+          throw StoneException(ErrorCode_WebGLContextLost);
         }
 
         if (emscripten_webgl_make_context_current(context_) != EMSCRIPTEN_RESULT_SUCCESS)
@@ -248,11 +248,6 @@
       return pimpl_->IsContextLost();
     }
 
-    void WebAssemblyOpenGLContext::RestoreLostContext()
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
-    }
-
     void WebAssemblyOpenGLContext::SetLostContext()
     {
       pimpl_->SetLostContext();
--- a/Framework/OpenGL/WebAssemblyOpenGLContext.h	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/OpenGL/WebAssemblyOpenGLContext.h	Tue Oct 15 10:54:53 2019 +0200
@@ -58,9 +58,6 @@
 
       virtual bool IsContextLost() ORTHANC_OVERRIDE;
 
-      virtual void SetLostContext() ORTHANC_OVERRIDE;
-      virtual void RestoreLostContext() ORTHANC_OVERRIDE;
-
       virtual void MakeCurrent() ORTHANC_OVERRIDE;
 
       virtual void SwapBuffer() ORTHANC_OVERRIDE;
@@ -69,8 +66,6 @@
 
       virtual unsigned int GetCanvasHeight() const ORTHANC_OVERRIDE;
 
-      virtual void* DebugGetInternalContext() const ORTHANC_OVERRIDE;
-
       /**
       Returns true if the underlying context has been successfully recreated
       */
@@ -79,6 +74,13 @@
       void UpdateSize();
 
       const std::string& GetCanvasIdentifier() const;
+
+
+      /**
+       * This is for manual context loss (debug purposes)
+       **/
+      void* DebugGetInternalContext() const;
+      void SetLostContext();
     };
   }
 }
--- a/Framework/Radiography/RadiographyScene.h	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Radiography/RadiographyScene.h	Tue Oct 15 10:54:53 2019 +0200
@@ -263,9 +263,9 @@
 
     Extent2D GetSceneExtent() const;
 
-    void Render(Orthanc::ImageAccessor& buffer,
-                const AffineTransform2D& viewTransform,
-                ImageInterpolation interpolation) const;
+    virtual void Render(Orthanc::ImageAccessor& buffer,
+                        const AffineTransform2D& viewTransform,
+                        ImageInterpolation interpolation) const;
 
     bool LookupLayer(size_t& index /* out */,
                      double x,
--- a/Framework/Radiography/RadiographyWindowingTracker.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Radiography/RadiographyWindowingTracker.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -20,6 +20,7 @@
 
 
 #include "RadiographyWindowingTracker.h"
+#include "RadiographyWidget.h"
 
 #include <Core/OrthancException.h>
 
@@ -115,6 +116,8 @@
     
   RadiographyWindowingTracker::RadiographyWindowingTracker(UndoRedoStack& undoRedoStack,
                                                            RadiographyScene& scene,
+                                                           RadiographyWidget& widget,
+                                                           ImageInterpolation interpolationDuringTracking,
                                                            int x,
                                                            int y,
                                                            Action leftAction,
@@ -123,6 +126,8 @@
                                                            Action downAction) :
     undoRedoStack_(undoRedoStack),
     scene_(scene),
+    widget_(widget),
+    initialWidgetInterpolation_(widget.GetInterpolation()),
     clickX_(x),
     clickY_(y),
     leftAction_(leftAction),
@@ -131,6 +136,7 @@
     downAction_(downAction)
   {
     scene_.GetWindowingWithDefault(sourceCenter_, sourceWidth_);
+    widget_.SetInterpolation(interpolationDuringTracking);
 
     float minValue, maxValue;
     scene.GetRange(minValue, maxValue);
@@ -156,6 +162,7 @@
 
   void RadiographyWindowingTracker::MouseUp()
   {
+    widget_.SetInterpolation(initialWidgetInterpolation_);
     undoRedoStack_.Add(new UndoRedoCommand(*this));
   }
 
--- a/Framework/Radiography/RadiographyWindowingTracker.h	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Radiography/RadiographyWindowingTracker.h	Tue Oct 15 10:54:53 2019 +0200
@@ -27,6 +27,9 @@
 
 namespace OrthancStone
 {
+
+  class RadiographyWidget;
+
   class RadiographyWindowingTracker : public Deprecated::IWorldSceneMouseTracker
   {   
   public:
@@ -43,6 +46,8 @@
 
     UndoRedoStack&      undoRedoStack_;
     RadiographyScene&   scene_;
+    RadiographyWidget&  widget_;
+    ImageInterpolation  initialWidgetInterpolation_;
     int                 clickX_;
     int                 clickY_;
     Action              leftAction_;
@@ -62,6 +67,8 @@
   public:
     RadiographyWindowingTracker(UndoRedoStack& undoRedoStack,
                                 RadiographyScene& scene,
+                                RadiographyWidget& widget,
+                                ImageInterpolation interpolationDuringTracking,
                                 int x,
                                 int y,
                                 Action leftAction,
--- a/Framework/Scene2D/ICompositor.h	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Scene2D/ICompositor.h	Tue Oct 15 10:54:53 2019 +0200
@@ -1,29 +1,29 @@
-#pragma once
-
-#include <boost/noncopyable.hpp>
-#include <EmbeddedResources.h>
-#include <Core/Enumerations.h>
-
-namespace OrthancStone
-{
-  class ICompositor : public boost::noncopyable
-  {
-
-  public:
-    virtual ~ICompositor() {}
-
-    virtual unsigned int GetCanvasWidth() const = 0;
-
-    virtual unsigned int GetCanvasHeight() const = 0;
-
-    virtual void Refresh() = 0;
-
-#if ORTHANC_ENABLE_LOCALE == 1
-    virtual void SetFont(size_t index,
-                         Orthanc::EmbeddedResources::FileResourceId resource,
-                         unsigned int fontSize,
-                         Orthanc::Encoding codepage) = 0;
-#endif
-
-  };
-}
+#pragma once
+
+#include <boost/noncopyable.hpp>
+#include <EmbeddedResources.h>
+#include <Core/Enumerations.h>
+
+namespace OrthancStone
+{
+  class ICompositor : public boost::noncopyable
+  {
+  public:
+    virtual ~ICompositor() 
+    {
+    }
+
+    virtual unsigned int GetCanvasWidth() const = 0;
+
+    virtual unsigned int GetCanvasHeight() const = 0;
+
+    virtual void Refresh() = 0;
+
+#if ORTHANC_ENABLE_LOCALE == 1
+    virtual void SetFont(size_t index,
+                         Orthanc::EmbeddedResources::FileResourceId resource,
+                         unsigned int fontSize,
+                         Orthanc::Encoding codepage) = 0;
+#endif
+  };
+}
--- a/Framework/Scene2D/Internals/OpenGLLookupTableTextureRenderer.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Scene2D/Internals/OpenGLLookupTableTextureRenderer.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -21,6 +21,8 @@
 
 #include "OpenGLLookupTableTextureRenderer.h"
 
+#include <Core/OrthancException.h>
+
 namespace OrthancStone
 {
   namespace Internals
--- a/Framework/Scene2DViewport/AngleMeasureTool.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Scene2DViewport/AngleMeasureTool.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -22,6 +22,7 @@
 #include "MeasureToolsToolbox.h"
 #include "EditAngleMeasureTracker.h"
 #include "LayerHolder.h"
+#include "../StoneException.h"
 
 #include <Core/Logging.h>
 
--- a/Framework/Scene2DViewport/EditAngleMeasureTracker.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Scene2DViewport/EditAngleMeasureTracker.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -21,6 +21,8 @@
 #include "EditAngleMeasureTracker.h"
 #include "EditAngleMeasureCommand.h"
 
+#include "../StoneException.h"
+
 namespace OrthancStone
 {
   EditAngleMeasureTracker::EditAngleMeasureTracker(
--- a/Framework/Scene2DViewport/EditLineMeasureTracker.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Scene2DViewport/EditLineMeasureTracker.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -21,6 +21,8 @@
 #include "EditLineMeasureTracker.h"
 #include "EditLineMeasureCommand.h"
 
+#include "../StoneException.h"
+
 
 namespace OrthancStone
 {
--- a/Framework/Scene2DViewport/LineMeasureTool.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Scene2DViewport/LineMeasureTool.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -22,6 +22,7 @@
 #include "MeasureToolsToolbox.h"
 #include "EditLineMeasureTracker.h"
 #include "LayerHolder.h"
+#include "../StoneException.h"
 
 #include <Core/Logging.h>
 
--- a/Framework/Scene2DViewport/MeasureCommands.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Scene2DViewport/MeasureCommands.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -71,8 +71,8 @@
 
   DeleteMeasureCommand::DeleteMeasureCommand(boost::shared_ptr<MeasureTool> measureTool, boost::weak_ptr<ViewportController> controllerW)
     : MeasureCommand(controllerW)
+    , mementoOriginal_(measureTool->GetMemento())
     , measureTool_(measureTool)
-    , mementoOriginal_(measureTool->GetMemento())
     , mementoModified_(measureTool->GetMemento())
   {
     GetMeasureTool()->Disable();
--- a/Framework/Scene2DViewport/MeasureToolsToolbox.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Scene2DViewport/MeasureToolsToolbox.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -25,6 +25,7 @@
 
 #include "../Scene2D/TextSceneLayer.h"
 #include "../Scene2D/Scene2D.h"
+#include "../StoneException.h"
 
 #include <boost/math/constants/constants.hpp>
 
--- a/Framework/Scene2DViewport/ViewportController.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Scene2DViewport/ViewportController.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -113,7 +113,7 @@
   
   bool ViewportController::HandlePointerEvent(PointerEvent e)
   {
-    throw StoneException(ErrorCode_NotImplemented);
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
   }
 
   std::vector<boost::shared_ptr<MeasureTool> > ViewportController::HitTestMeasureTools(
@@ -168,8 +168,12 @@
 
   void ViewportController::FitContent()
   {
-    viewport_.GetScene().FitContent(viewport_.GetCanvasWidth(), viewport_.GetCanvasHeight());
-    BroadcastMessage(SceneTransformChanged(*this));
+    if (viewport_.HasCompositor())
+    {
+      const ICompositor& compositor = viewport_.GetCompositor();
+      viewport_.GetScene().FitContent(compositor.GetCanvasWidth(), compositor.GetCanvasHeight());
+      BroadcastMessage(SceneTransformChanged(*this));
+    }
   }
 
   void ViewportController::AddMeasureTool(boost::shared_ptr<MeasureTool> measureTool)
@@ -219,4 +223,3 @@
     return TEXT_CENTER_DISTANCE_CANVAS_COORD * GetCanvasToSceneFactor();
   }
 }
-
--- a/Framework/StoneException.h	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/StoneException.h	Tue Oct 15 10:54:53 2019 +0200
@@ -36,9 +36,6 @@
     ErrorCode_ApplicationException, // this StoneException is specific to an application (and should have its own internal error code)
     ErrorCode_NotImplemented, // case not implemented
 
-    ErrorCode_PromiseSingleSuccessHandler, // a Promise can only have a single success handler
-    ErrorCode_PromiseSingleFailureHandler, // a Promise can only have a single failure handler
-
     ErrorCode_CanOnlyAddOneLayerAtATime,
     ErrorCode_CommandJsonInvalidFormat,
     ErrorCode_WebGLContextLost,
@@ -46,7 +43,6 @@
   };
 
 
-
   class StoneException
   {
   protected:
@@ -70,71 +66,6 @@
       return "TODO: EnumerationToString for StoneException";
     }
   };
-
-  class OpenGLContextLostException : public StoneException
-  {
-  public:
-    explicit OpenGLContextLostException(void* context)
-      : StoneException(ErrorCode_WebGLContextLost)
-      , context_(context)
-    {
-    }
-    virtual const char* What() const
-    {
-      return "The OpenGL/WebGL context has been lost!";
-    }
-    void* context_;
-  };
-
-  class StoneOrthancException : public StoneException
-  {
-  protected:
-    Orthanc::OrthancException&  orthancException_;
-
-  public:
-    explicit StoneOrthancException(Orthanc::OrthancException& orthancException) :
-      StoneException(ErrorCode_OrthancError),
-      orthancException_(orthancException)
-    {
-    }
-
-    Orthanc::ErrorCode GetOrthancErrorCode() const
-    {
-      return orthancException_.GetErrorCode();
-    }
-
-    virtual const char* What() const
-    {
-      return orthancException_.What();
-    }
-  };
-
-  class StoneApplicationException : public StoneException
-  {
-  protected:
-    int applicationErrorCode_;
-    mutable std::string errorMessage_;
-
-  public:
-    explicit StoneApplicationException(int applicationErrorCode) :
-      StoneException(ErrorCode_ApplicationException),
-      applicationErrorCode_(applicationErrorCode)
-    {
-    }
-
-    int GetApplicationErrorCode() const
-    {
-      return applicationErrorCode_;
-    }
-
-    virtual const char* What() const
-    {
-      if (errorMessage_.size() == 0)
-        errorMessage_ = boost::lexical_cast<std::string>(applicationErrorCode_);
-
-      return errorMessage_.c_str();
-    }
-  };
 }
 
 // See https://isocpp.org/wiki/faq/misc-technical-issues#macros-with-multi-stmts
@@ -163,14 +94,6 @@
 
 
 
-
-
-
-
-
-
-
-
 /*
 Explanation:
 
--- a/Framework/StoneInitialization.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/StoneInitialization.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -28,7 +28,7 @@
 #endif
 
 #if ORTHANC_ENABLE_SDL == 1
-#  include "../Applications/Sdl/SdlWindow.h"
+#  include "Viewport/SdlWindow.h"
 #endif
 
 #if ORTHANC_ENABLE_CURL == 1
--- a/Framework/Toolbox/AffineTransform2D.h	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Toolbox/AffineTransform2D.h	Tue Oct 15 10:54:53 2019 +0200
@@ -77,6 +77,7 @@
                                      const AffineTransform2D& c,
                                      const AffineTransform2D& d);
 
+    // transformations are applied right to left: e is the first transfo applied, a is the last one
     static AffineTransform2D Combine(const AffineTransform2D& a,
                                      const AffineTransform2D& b,
                                      const AffineTransform2D& c,
--- a/Framework/Viewport/IViewport.h	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Viewport/IViewport.h	Tue Oct 15 10:54:53 2019 +0200
@@ -42,32 +42,10 @@
 
     virtual void Refresh() = 0;
 
-    virtual unsigned int GetCanvasWidth() const = 0;
-
-    virtual unsigned int GetCanvasHeight() const = 0;
-
-    virtual const std::string& GetCanvasIdentifier() const = 0;
-
-    virtual ScenePoint2D GetPixelCenterCoordinates(int x, int y) const = 0;
-
-    virtual bool IsContextLost() = 0;
-
-    virtual void* DebugGetInternalContext() const = 0;
+    virtual ScenePoint2D GetPixelCenterCoordinates(int x, int y) = 0;
 
-#if ORTHANC_ENABLE_LOCALE == 1
-    virtual void SetFont(size_t index,
-      Orthanc::EmbeddedResources::FileResourceId resource,
-      unsigned int fontSize,
-      Orthanc::Encoding codepage) = 0;
-#endif
+    virtual bool HasCompositor() const = 0;
 
-  protected:
-    virtual ICompositor* GetCompositor() = 0;
-
-    virtual const ICompositor* GetCompositor() const
-    {
-      IViewport* self = const_cast<IViewport*>(this);
-      return self->GetCompositor();
-    }
+    virtual ICompositor& GetCompositor() = 0;
   };
 }
--- a/Framework/Viewport/SdlViewport.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Viewport/SdlViewport.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -30,7 +30,6 @@
                                        unsigned int width,
                                        unsigned int height,
                                        bool allowDpiScaling) :
-    SdlViewport(title),
     context_(title, width, height, allowDpiScaling)
   {
     compositor_.reset(new OpenGLCompositor(context_, GetScene()));
@@ -41,134 +40,28 @@
                                        unsigned int height,
                                        boost::shared_ptr<Scene2D>& scene,
                                        bool allowDpiScaling) :
-    SdlViewport(title, scene),
+    SdlViewport(scene),
     context_(title, width, height, allowDpiScaling)
   {
     compositor_.reset(new OpenGLCompositor(context_, GetScene()));
   }
 
-
-  void* SdlOpenGLViewport::DebugGetInternalContext() const
-  {
-    return context_.DebugGetInternalContext();
-  }
-
-  bool SdlOpenGLViewport::IsContextLost() {
-    return context_.IsContextLost();
-  }
-
-  bool SdlOpenGLViewport::OpenGLContextLost()
-  {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
-  }
-
-  bool SdlOpenGLViewport::OpenGLContextRestored()
-  {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
-  }
-
-  void SdlOpenGLViewport::DisableCompositor()
-  {
-    compositor_.reset(NULL);
-  }
-
-  void SdlOpenGLViewport::RestoreCompositor()
-  {
-    // the context must have been restored!
-    ORTHANC_ASSERT(!context_.IsContextLost());
-
-    if (compositor_.get() == NULL)
-    {
-      compositor_.reset(new OpenGLCompositor(context_, GetScene()));
-    }
-    else
-    {
-      std::string windowTitle(SDL_GetWindowTitle(GetWindow().GetObject()));
-      LOG(WARNING) << "RestoreCompositor() called for \"" << windowTitle << "\" while it was NOT lost! Nothing done.";
-    }
-  }
-
-  // extern bool Debug_MustContextBeRestored(std::string title);
-  // extern void Debug_Context_ClearRestoreFlag(std::string title);
-  // extern void Debug_Context_ClearKillFlag(std::string title);
-
-  bool Debug_SdlOpenGLViewport_Refresh_BP = false;
-
   void SdlOpenGLViewport::Refresh()
   {
-    // <DEBUG CODE USED FOR CONTEXT LOSS RESTORING>
-    // try to restore the context if requested
-    // Debug_Context_ClearRestoreFlag
-    // Debug_SdlOpenGLViewport_Refresh_BP = true;
-    // try
-    // {
-    //   if (Debug_MustContextBeRestored(GetCanvasIdentifier()))
-    //   {
-    //     // to prevent a bug where the context is both labelled as "to be lost" and "to be restored"
-    //     // (occurs when one is hammering away at the keyboard like there's no tomorrow)
-    //     Debug_Context_ClearKillFlag(GetCanvasIdentifier());
-    //     // this is called manually for loss/restore simulation
-    //     context_.RestoreLostContext();
-    //     RestoreCompositor();
-    //     Debug_Context_ClearRestoreFlag(GetCanvasIdentifier());
-    //   }
-    // }
-    // catch (const OpenGLContextLostException& e)
-    // {
-    //   LOG(ERROR) << "OpenGLContextLostException in SdlOpenGLViewport::Refresh() part 1";
-    // }
-    // Debug_SdlOpenGLViewport_Refresh_BP = false;
-    // </DEBUG CODE USED FOR CONTEXT LOSS RESTORING>
-
-    try
-    {
-      // the compositor COULD be dead!
-      if (GetCompositor())
-        GetCompositor()->Refresh();
-    }
-    catch (const OpenGLContextLostException& e)
-    {
-      // we need to wait for the "context restored" callback
-      LOG(WARNING) << "Context " << std::hex << e.context_ << " is lost! Compositor will be disabled.";
-      DisableCompositor();
-
-      // <DEBUG CODE USED FOR CONTEXT LOSS RESTORING>
-      // in case this was externally triggered...
-      //Debug_Context_ClearKillFlag(GetCanvasIdentifier());
-      // </DEBUG CODE USED FOR CONTEXT LOSS RESTORING>
-    }
-    catch (...)
-    {
-      // something else nasty happened
-      throw;
-    }
+    compositor_->Refresh();
   }
 
 
-
-
-
   SdlCairoViewport::SdlCairoViewport(const char* title,
                                      unsigned int width,
                                      unsigned int height,
                                      bool allowDpiScaling) :
-    SdlViewport(title),
     window_(title, width, height, false /* enable OpenGL */, allowDpiScaling),
     compositor_(GetScene(), width, height)
   {
     UpdateSdlSurfaceSize(width, height);
   }
 
-  void SdlCairoViewport::DisableCompositor()
-  {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
-  }
-  
-  void SdlCairoViewport::RestoreCompositor()
-  {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
-  }
-
   SdlCairoViewport::~SdlCairoViewport()
   {
     if (sdlSurface_)
@@ -179,7 +72,7 @@
   
   void SdlCairoViewport::Refresh()
   {
-    GetCompositor()->Refresh();
+    compositor_.Refresh();
     window_.Render(sdlSurface_);
   }
 
@@ -206,5 +99,4 @@
       throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
     }
   }
-
 }
--- a/Framework/Viewport/SdlViewport.h	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Viewport/SdlViewport.h	Tue Oct 15 10:54:53 2019 +0200
@@ -36,7 +36,7 @@
 #  error Support for OpenGL is disabled
 #endif
 
-#include "../../Applications/Sdl/SdlOpenGLContext.h"
+#include "../OpenGL/SdlOpenGLContext.h"
 #include "../Scene2D/OpenGLCompositor.h"
 #include "../Scene2D/CairoCompositor.h"
 #include "ViewportBase.h"
@@ -46,23 +46,21 @@
   class SdlViewport : public ViewportBase
   {
   public:
-    SdlViewport(const std::string& identifier)
-      : ViewportBase(identifier)
-    {}
+    SdlViewport()
+    {
+    }
 
-    SdlViewport(const std::string& identifier,
-                boost::shared_ptr<Scene2D>& scene)
-      : ViewportBase(identifier, scene)
+    SdlViewport(boost::shared_ptr<Scene2D>& scene) : 
+      ViewportBase(scene)
     {
-
     }
 
     virtual SdlWindow& GetWindow() = 0;
     
     virtual void UpdateSize(unsigned int width,
                             unsigned int height) = 0;
+  };
 
-  };
 
   class SdlOpenGLViewport : public SdlViewport
   {
@@ -87,25 +85,21 @@
       return context_.GetWindow();
     }
 
-    virtual void* DebugGetInternalContext() const ORTHANC_OVERRIDE;
-
-    virtual bool IsContextLost() ORTHANC_OVERRIDE;
-    bool OpenGLContextLost();
-    bool OpenGLContextRestored();
+    virtual void Refresh() ORTHANC_OVERRIDE;
 
     virtual void UpdateSize(unsigned int width, unsigned int height) ORTHANC_OVERRIDE
     {
       // nothing to do in OpenGL, the OpenGLCompositor::UpdateSize will be called automatically
     }
-    virtual void Refresh() ORTHANC_OVERRIDE;
 
-  protected:
-    virtual void DisableCompositor() ORTHANC_OVERRIDE;
-    virtual void RestoreCompositor() ORTHANC_OVERRIDE;
+    virtual bool HasCompositor() const ORTHANC_OVERRIDE
+    {
+      return true;
+    }
 
-    virtual ICompositor* GetCompositor() ORTHANC_OVERRIDE
+    virtual ICompositor& GetCompositor() ORTHANC_OVERRIDE
     {
-      return compositor_.get();
+      return *compositor_.get();
     }
   };
 
@@ -117,6 +111,10 @@
     CairoCompositor   compositor_;
     SDL_Surface*      sdlSurface_;
 
+  private:
+    void UpdateSdlSurfaceSize(unsigned int width,
+                              unsigned int height);
+
   public:
     SdlCairoViewport(const char* title,
                      unsigned int width,
@@ -135,22 +133,20 @@
     {
       return window_;
     }
-
-    virtual void DisableCompositor() ORTHANC_OVERRIDE;
-    virtual void RestoreCompositor() ORTHANC_OVERRIDE;
-
+    
     virtual void Refresh() ORTHANC_OVERRIDE;
 
     virtual void UpdateSize(unsigned int width,
                             unsigned int height) ORTHANC_OVERRIDE;
-  protected:
-    virtual ICompositor* GetCompositor() ORTHANC_OVERRIDE
+
+    virtual bool HasCompositor() const ORTHANC_OVERRIDE
     {
-      return &compositor_;
+      return true;
     }
 
-  private:
-    void UpdateSdlSurfaceSize(unsigned int width,
-                              unsigned int height);
+    virtual ICompositor& GetCompositor() ORTHANC_OVERRIDE
+    {
+      return compositor_;
+    }
   };
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Viewport/SdlWindow.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -0,0 +1,201 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 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 "SdlWindow.h"
+
+#if ORTHANC_ENABLE_SDL == 1
+
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+
+#ifdef WIN32 
+#include <windows.h> // for SetProcessDpiAware
+#endif 
+// WIN32
+
+#include <SDL.h>
+
+namespace OrthancStone
+{
+  SdlWindow::SdlWindow(const char* title,
+                       unsigned int width,
+                       unsigned int height,
+                       bool enableOpenGl,
+                       bool allowDpiScaling) :
+    maximized_(false)
+  {
+    // TODO Understand why, with SDL_WINDOW_OPENGL + MinGW32 + Release
+    // build mode, the application crashes whenever the SDL window is
+    // resized or maximized
+
+    uint32_t windowFlags, rendererFlags;
+    if (enableOpenGl)
+    {
+      windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE;
+      rendererFlags = SDL_RENDERER_ACCELERATED;
+    }
+    else
+    {
+      windowFlags = SDL_WINDOW_RESIZABLE;
+      rendererFlags = SDL_RENDERER_SOFTWARE;
+    }
+
+// TODO: probably required on MacOS X, too
+#if defined(WIN32) && (_WIN32_WINNT >= 0x0600)
+    if (!allowDpiScaling)
+    {
+      // if we do NOT allow DPI scaling, it means an SDL pixel will be a real
+      // monitor pixel. This is needed for high-DPI applications
+
+      // Enable high-DPI support on Windows
+
+      // THE FOLLOWING HAS BEEN COMMENTED OUT BECAUSE IT WILL CRASH UNDER 
+      // OLD WINDOWS VERSIONS
+      // ADD THIS AT THE TOP TO ENABLE IT:
+      // 
+      //#pragma comment(lib, "Shcore.lib") THIS IS ONLY REQUIRED FOR SetProcessDpiAwareness
+      //#include <windows.h>
+      //#include <ShellScalingAPI.h> THIS IS ONLY REQUIRED FOR SetProcessDpiAwareness
+      //#include <comdef.h> THIS IS ONLY REQUIRED FOR SetProcessDpiAwareness
+      // SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
+      
+      // This is supported on Vista+
+      SetProcessDPIAware();
+
+      windowFlags |= SDL_WINDOW_ALLOW_HIGHDPI;
+    }
+#endif 
+// WIN32
+    
+    window_ = SDL_CreateWindow(title,
+                               SDL_WINDOWPOS_UNDEFINED,
+                               SDL_WINDOWPOS_UNDEFINED,
+                               width, height, windowFlags);
+
+    if (window_ == NULL) 
+    {
+      LOG(ERROR) << "Cannot create the SDL window: " << SDL_GetError();
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+
+    renderer_ = SDL_CreateRenderer(window_, -1, rendererFlags);
+    if (!renderer_)
+    {
+      LOG(ERROR) << "Cannot create the SDL renderer: " << SDL_GetError();
+      SDL_DestroyWindow(window_);
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+  }
+
+
+  SdlWindow::~SdlWindow()
+  {
+    if (renderer_ != NULL)
+    { 
+      SDL_DestroyRenderer(renderer_);
+    }
+
+    if (window_ != NULL)
+    { 
+      SDL_DestroyWindow(window_);
+    }
+  }
+
+
+  unsigned int SdlWindow::GetWidth() const
+  {
+    int w = -1;
+    SDL_GetWindowSize(window_, &w, NULL);
+
+    if (w < 0)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+    else
+    {
+      return static_cast<unsigned int>(w);
+    }
+  }
+
+
+  unsigned int SdlWindow::GetHeight() const
+  {
+    int h = -1;
+    SDL_GetWindowSize(window_, NULL, &h);
+
+    if (h < 0)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+    else
+    {
+      return static_cast<unsigned int>(h);
+    }
+  }
+
+
+  void SdlWindow::Render(SDL_Surface* surface)
+  {
+    //SDL_RenderClear(renderer_);
+
+    SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer_, surface);
+    if (texture != NULL)
+    {
+      SDL_RenderCopy(renderer_, texture, NULL, NULL);
+      SDL_DestroyTexture(texture);
+    }
+
+    SDL_RenderPresent(renderer_);
+  }
+
+
+  void SdlWindow::ToggleMaximize()
+  {
+    if (maximized_)
+    {
+      SDL_RestoreWindow(window_);
+      maximized_ = false;
+    }
+    else
+    {
+      SDL_MaximizeWindow(window_);
+      maximized_ = true;
+    }
+  }
+
+
+  void SdlWindow::GlobalInitialize()
+  {
+    if (SDL_Init(SDL_INIT_VIDEO) != 0)
+    {
+      LOG(ERROR) << "Cannot initialize SDL";
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+  }
+
+
+  void SdlWindow::GlobalFinalize()
+  {
+    SDL_Quit();
+  }
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Viewport/SdlWindow.h	Tue Oct 15 10:54:53 2019 +0200
@@ -0,0 +1,67 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 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
+
+#if ORTHANC_ENABLE_SDL == 1
+
+#include <SDL_render.h>
+#include <SDL_video.h>
+#include <boost/noncopyable.hpp>
+
+namespace OrthancStone
+{
+  class SdlWindow : public boost::noncopyable
+  {
+  private:
+    SDL_Window    *window_;
+    SDL_Renderer  *renderer_;
+    bool           maximized_;
+
+  public:
+    SdlWindow(const char* title,
+              unsigned int width,
+              unsigned int height,
+              bool enableOpenGl,
+              bool allowDpiScaling = true);
+
+    ~SdlWindow();
+
+    SDL_Window *GetObject() const
+    {
+      return window_;
+    }
+
+    unsigned int GetWidth() const;
+
+    unsigned int GetHeight() const;
+
+    void Render(SDL_Surface* surface);
+
+    void ToggleMaximize();
+
+    static void GlobalInitialize();
+
+    static void GlobalFinalize();
+  };
+}
+
+#endif
--- a/Framework/Viewport/ViewportBase.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Viewport/ViewportBase.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -26,16 +26,13 @@
 
 namespace OrthancStone
 {
-  ViewportBase::ViewportBase(const std::string& identifier) :
-    identifier_(identifier),
+  ViewportBase::ViewportBase() :
     scene_(boost::make_shared<Scene2D>())
   {
   }
 
   
-  ViewportBase::ViewportBase(const std::string& identifier,
-                             boost::shared_ptr<Scene2D>& scene) :
-    identifier_(identifier),
+  ViewportBase::ViewportBase(boost::shared_ptr<Scene2D>& scene) :
     scene_(scene)
   {
     if (scene.get() == NULL)
@@ -45,10 +42,18 @@
   }
   
 
-  ScenePoint2D ViewportBase::GetPixelCenterCoordinates(int x, int y) const
+  ScenePoint2D ViewportBase::GetPixelCenterCoordinates(int x, int y)
   {
-    return ScenePoint2D(
-      static_cast<double>(x) + 0.5 - static_cast<double>(GetCanvasWidth()) / 2.0,
-      static_cast<double>(y) + 0.5 - static_cast<double>(GetCanvasHeight()) / 2.0);
+    if (HasCompositor())
+    {
+      const ICompositor& compositor = GetCompositor();
+      return ScenePoint2D(
+        static_cast<double>(x) + 0.5 - static_cast<double>(compositor.GetCanvasWidth()) / 2.0,
+        static_cast<double>(y) + 0.5 - static_cast<double>(compositor.GetCanvasHeight()) / 2.0);
+    }
+    else
+    {
+      return ScenePoint2D(0, 0);
+    }
   }
 }
--- a/Framework/Viewport/ViewportBase.h	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Viewport/ViewportBase.h	Tue Oct 15 10:54:53 2019 +0200
@@ -28,55 +28,19 @@
 {
   class ViewportBase : public IViewport
   {
-  public:
-    ViewportBase(const std::string& identifier);
+  private:
+    boost::shared_ptr<Scene2D>  scene_;
 
-    ViewportBase(const std::string& identifier,
-                 boost::shared_ptr<Scene2D>& scene);
+  public:
+    ViewportBase();
+
+    ViewportBase(boost::shared_ptr<Scene2D>& scene);
 
     virtual Scene2D& GetScene() ORTHANC_OVERRIDE
     {
       return *scene_;
     }
 
-    virtual const std::string& GetCanvasIdentifier() const ORTHANC_OVERRIDE
-    {
-      return identifier_;
-    }
-
-    virtual ScenePoint2D GetPixelCenterCoordinates(int x, int y) const ORTHANC_OVERRIDE;
-
-    virtual unsigned int GetCanvasWidth() const ORTHANC_OVERRIDE
-    {
-      if (GetCompositor())
-        return GetCompositor()->GetCanvasWidth();
-      else
-        return 0;
-    }
-
-    virtual unsigned int GetCanvasHeight() const ORTHANC_OVERRIDE
-    {
-      if (GetCompositor())
-        return GetCompositor()->GetCanvasHeight();
-      else
-        return 0;
-    }
-
-#if ORTHANC_ENABLE_LOCALE == 1
-    virtual void SetFont(size_t index,
-      Orthanc::EmbeddedResources::FileResourceId resource,
-      unsigned int fontSize,
-      Orthanc::Encoding codepage) ORTHANC_OVERRIDE
-    {
-      return GetCompositor()->SetFont(index, resource, fontSize, codepage);
-    }
-#endif
-
-  private:
-    std::string                 identifier_;
-    boost::shared_ptr<Scene2D>  scene_;
-  protected:
-    virtual void RestoreCompositor() = 0;
-    virtual void DisableCompositor() = 0;
+    virtual ScenePoint2D GetPixelCenterCoordinates(int x, int y) ORTHANC_OVERRIDE;
   };
 }
--- a/Framework/Viewport/WebAssemblyViewport.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Viewport/WebAssemblyViewport.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -20,6 +20,9 @@
 
 
 #include "WebAssemblyViewport.h"
+
+#include "../StoneException.h"
+
 #include <emscripten/html5.h>
 
 namespace OrthancStone
@@ -82,14 +85,31 @@
 
   void WebAssemblyOpenGLViewport::DisableCompositor()
   {
-    compositor_.reset(NULL);
+    compositor_.reset();
+  }
+
+  ICompositor& WebAssemblyOpenGLViewport::GetCompositor()
+  {
+    if (compositor_.get() == NULL)
+    {
+      // "HasCompositor()" should have been called
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      return *compositor_;
+    }
   }
 
   void WebAssemblyOpenGLViewport::Refresh()
   {
     try
     {
-      if (!GetCompositor())
+      if (HasCompositor())
+      {
+        GetCompositor().Refresh();
+      }
+      else
       {
         // this block was added because of (perceived?) bugs in the 
         // browser where the WebGL contexts are NOT automatically restored 
@@ -99,22 +119,27 @@
         //LOG(ERROR) << "About to call WebAssemblyOpenGLContext::TryRecreate().";
         //LOG(ERROR) << "Before calling it, isContextLost == " << context_.IsContextLost();
 
-        if (!context_.IsContextLost()) {
+        if (!context_.IsContextLost())
+        {
           LOG(TRACE) << "Context restored!";
           //LOG(ERROR) << "After calling it, isContextLost == " << context_.IsContextLost();
           RestoreCompositor();
           UpdateSize();
         }
       }
-      if (GetCompositor()) {
-        GetCompositor()->Refresh();
+    }
+    catch (const StoneException& e)
+    {
+      if (e.GetErrorCode() == ErrorCode_WebGLContextLost)
+      {
+        LOG(WARNING) << "Context is lost! Compositor will be disabled.";
+        DisableCompositor();
+        // we now need to wait for the "context restored" callback
       }
-    }
-    catch (const OpenGLContextLostException& e)
-    {
-      LOG(WARNING) << "Context " << std::hex << e.context_ << " is lost! Compositor will be disabled.";
-      DisableCompositor();
-      // we now need to wait for the "context restored" callback
+      else
+      {
+        throw;
+      }
     }
     catch (...)
     {
@@ -122,11 +147,6 @@
       throw;
     }
   }
-  
-  bool WebAssemblyOpenGLViewport::IsContextLost()
-  {
-    return context_.IsContextLost();
-  }
 
   void WebAssemblyOpenGLViewport::RestoreCompositor()
   {
@@ -155,7 +175,7 @@
     
     // maybe the context has already been restored by other means (the 
     // Refresh() function)
-    if (!GetCompositor())
+    if (!HasCompositor())
     {
       RestoreCompositor();
       UpdateSize();
@@ -163,11 +183,6 @@
     return false;
   }
 
-  void* WebAssemblyOpenGLViewport::DebugGetInternalContext() const
-  {
-    return context_.DebugGetInternalContext();
-  }
-
   void WebAssemblyOpenGLViewport::RegisterContextCallbacks()
   {
 #if 0
@@ -248,6 +263,6 @@
   void WebAssemblyCairoViewport::Refresh()
   {
     LOG(INFO) << "refreshing cairo viewport, TODO: blit to the canvans.getContext('2d')";
-    GetCompositor()->Refresh();
+    GetCompositor().Refresh();
   }
 }
--- a/Framework/Viewport/WebAssemblyViewport.h	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Viewport/WebAssemblyViewport.h	Tue Oct 15 10:54:53 2019 +0200
@@ -30,19 +30,29 @@
 {
   class WebAssemblyViewport : public ViewportBase
   {
+  private:
+    std::string  canvasIdentifier_;
+
   public:
-    WebAssemblyViewport(const std::string& identifier)
-      : ViewportBase(identifier)
+    WebAssemblyViewport(const std::string& canvasIdentifier) :
+      canvasIdentifier_(canvasIdentifier)
     {
     }
 
-    WebAssemblyViewport(const std::string& identifier,
-                        boost::shared_ptr<Scene2D>& scene)
-      : ViewportBase(identifier, scene)
+    WebAssemblyViewport(const std::string& canvasIdentifier,
+                        boost::shared_ptr<Scene2D>& scene) :
+      ViewportBase(scene),
+      canvasIdentifier_(canvasIdentifier)
     {
     }
+
+    const std::string& GetCanvasIdentifier() const
+    {
+      return canvasIdentifier_;
+    }
   };
 
+
   class WebAssemblyOpenGLViewport : public WebAssemblyViewport
   {
   private:
@@ -58,15 +68,15 @@
     // This function must be called each time the browser window is resized
     void UpdateSize();
 
-    virtual ICompositor* GetCompositor() ORTHANC_OVERRIDE
+    virtual bool HasCompositor() const ORTHANC_OVERRIDE
     {
-      return compositor_.get();
+      return (compositor_.get() != NULL);
     }
+    
+    virtual ICompositor& GetCompositor() ORTHANC_OVERRIDE;
 
     virtual void Refresh() ORTHANC_OVERRIDE;
 
-    virtual bool IsContextLost() ORTHANC_OVERRIDE;
-
     // this does NOT return whether the context is lost! This is called to 
     // tell Stone that the context has been lost
     bool OpenGLContextLost();
@@ -74,16 +84,14 @@
     // This should be called to indicate that the context has been lost
     bool OpenGLContextRestored();
 
-
-    virtual void* DebugGetInternalContext() const ORTHANC_OVERRIDE;
-
   private:
-    virtual void DisableCompositor() ORTHANC_OVERRIDE;
-    virtual void RestoreCompositor() ORTHANC_OVERRIDE;
+    void DisableCompositor();
+    void RestoreCompositor();
 
     void RegisterContextCallbacks();
   };
 
+
   class WebAssemblyCairoViewport : public WebAssemblyViewport
   {
   private:
@@ -98,12 +106,16 @@
     
     void UpdateSize(); 
 
-    virtual void Refresh();
+    virtual void Refresh() ORTHANC_OVERRIDE;
 
-    virtual ICompositor* GetCompositor()
+    virtual bool HasCompositor() const ORTHANC_OVERRIDE
     {
-      return &compositor_;
+      return true;
+    }
+    
+    virtual ICompositor& GetCompositor() ORTHANC_OVERRIDE
+    {
+      return compositor_;
     }
   };
-
 }
--- a/Framework/Volumes/DicomVolumeImage.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ b/Framework/Volumes/DicomVolumeImage.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -21,8 +21,6 @@
 
 #include "DicomVolumeImage.h"
 
-#include "../StoneException.h"
-
 #include <Core/OrthancException.h>
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/CMake/LinuxStandardBaseUic.py	Tue Oct 15 10:54:53 2019 +0200
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+
+import subprocess
+import sys
+
+if len(sys.argv) <= 1:
+    sys.stderr.write('Please provide arguments for uic\n')
+    sys.exit(-1)
+
+path = ''
+pos = 1
+while pos < len(sys.argv):
+    if sys.argv[pos].startswith('-'):
+        pos += 2
+    else:
+        path = sys.argv[pos]
+        break
+
+if len(path) == 0:
+    sys.stderr.write('Unable to find the input file in the arguments to uic\n')
+    sys.exit(-1)
+
+with open(path, 'r') as f:
+    lines = f.read().split('\n')
+    if (len(lines) > 1 and
+        lines[0].startswith('<?')):
+        content = '\n'.join(lines[1:])
+    else:
+        content = '\n'.join(lines)
+        
+# Remove the source file from the arguments
+args = sys.argv[1:pos] + sys.argv[pos+1:]
+
+p = subprocess.Popen([ '/opt/lsb/bin/uic' ] + args,
+                     stdin = subprocess.PIPE)
+p.communicate(input = content)
--- a/Resources/CMake/OrthancStoneConfiguration.cmake	Tue Oct 15 10:53:28 2019 +0200
+++ b/Resources/CMake/OrthancStoneConfiguration.cmake	Tue Oct 15 10:54:53 2019 +0200
@@ -275,17 +275,20 @@
     endif()
       
     if (ENABLE_SDL)
+      list(APPEND ORTHANC_STONE_SOURCES
+        ${ORTHANC_STONE_ROOT}/Framework/Viewport/SdlWindow.cpp
+        )
+
       list(APPEND APPLICATIONS_SOURCES
         ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlCairoSurface.cpp
         ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlEngine.cpp
         ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlOrthancSurface.cpp
         ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlStoneApplicationRunner.cpp
-        ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlWindow.cpp
         )
 
       if (ENABLE_OPENGL)
-        list(APPEND APPLICATIONS_SOURCES
-          ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlOpenGLContext.cpp
+        list(APPEND ORTHANC_STONE_SOURCES
+          ${ORTHANC_STONE_ROOT}/Framework/OpenGL/SdlOpenGLContext.cpp
           ${ORTHANC_STONE_ROOT}/Framework/Viewport/SdlViewport.cpp
           )
       endif()
@@ -449,10 +452,10 @@
   ${ORTHANC_STONE_ROOT}/Framework/Messages/ICallable.h
   ${ORTHANC_STONE_ROOT}/Framework/Messages/IMessage.h
   ${ORTHANC_STONE_ROOT}/Framework/Messages/IObservable.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Messages/IObserver.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Messages/IObserver.h
   ${ORTHANC_STONE_ROOT}/Framework/Messages/MessageBroker.h
   ${ORTHANC_STONE_ROOT}/Framework/Messages/MessageForwarder.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Messages/Promise.h
   ${ORTHANC_STONE_ROOT}/Framework/Oracle/GetOrthancImageCommand.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Oracle/GetOrthancWebViewerJpegCommand.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Oracle/OracleCommandWithPayload.cpp
--- a/Resources/CMake/QtConfiguration.cmake	Tue Oct 15 10:53:28 2019 +0200
+++ b/Resources/CMake/QtConfiguration.cmake	Tue Oct 15 10:54:53 2019 +0200
@@ -20,60 +20,137 @@
 set(CMAKE_AUTOMOC OFF)
 set(CMAKE_AUTOUIC OFF)
 
-# Find the QtWidgets library
-find_package(Qt5Widgets QUIET)
+if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase")
+  # Linux Standard Base version 5 ships Qt 4.2.3
+  include(Qt4Macros)
 
-if (Qt5Widgets_FOUND)
-  message("Qt5 has been detected")
-  find_package(Qt5Core REQUIRED)
-  link_libraries(
-    Qt5::Widgets
-    Qt5::Core
+  # The script "LinuxStandardBaseUic.py" is just a wrapper around the
+  # "uic" compiler from LSB that does not support the "<?xml ...?>"
+  # header that is automatically added by Qt Creator
+  set(QT_UIC_EXECUTABLE ${CMAKE_CURRENT_LIST_DIR}/LinuxStandardBaseUic.py)
+
+  set(QT_MOC_EXECUTABLE ${LSB_PATH}/bin/moc)
+
+  include_directories(
+    ${LSB_PATH}/include/QtCore
+    ${LSB_PATH}/include/QtGui
+    ${LSB_PATH}/include/QtOpenGL
     )
 
-  # Create aliases for the CMake commands
-  macro(ORTHANC_QT_WRAP_UI)
-    QT5_WRAP_UI(${ARGN})
-  endmacro()
+  link_libraries(QtCore QtGui QtOpenGL)
+
+
+  ##
+  ## This part is adapted from file "Qt4Macros.cmake" shipped with
+  ## CMake 3.5.1, released under the following license:
+  ##
+  ##=============================================================================
+  ## Copyright 2005-2009 Kitware, Inc.
+  ##
+  ## Distributed under the OSI-approved BSD License (the "License");
+  ## see accompanying file Copyright.txt for details.
+  ##
+  ## This software is distributed WITHOUT ANY WARRANTY; without even the
+  ## implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+  ## See the License for more information.
+  ##=============================================================================
+  ## 
+  macro (ORTHANC_QT_WRAP_UI outfiles)
+    QT4_EXTRACT_OPTIONS(ui_files ui_options ui_target ${ARGN})
+    foreach (it ${ui_files})
+      get_filename_component(outfile ${it} NAME_WE)
+      get_filename_component(infile ${it} ABSOLUTE)
+      set(outfile ${CMAKE_CURRENT_BINARY_DIR}/ui_${outfile}.h)
+      add_custom_command(OUTPUT ${outfile}
+        COMMAND ${QT_UIC_EXECUTABLE}
+        ARGS ${ui_options} -o ${outfile} ${infile}
+        MAIN_DEPENDENCY ${infile} VERBATIM)
+      set(${outfiles} ${${outfiles}} ${outfile})
+    endforeach ()
+  endmacro ()
   
-  macro(ORTHANC_QT_WRAP_CPP)
-    QT5_WRAP_CPP(${ARGN})
-  endmacro()
-    
+  macro (ORTHANC_QT_WRAP_CPP outfiles )
+    QT4_GET_MOC_FLAGS(moc_flags)
+    QT4_EXTRACT_OPTIONS(moc_files moc_options moc_target ${ARGN})
+    foreach (it ${moc_files})
+      get_filename_component(outfile ${it} NAME_WE)
+      get_filename_component(infile ${it} ABSOLUTE)
+      set(outfile ${CMAKE_CURRENT_BINARY_DIR}/moc_${outfile}.cxx)
+      add_custom_command(OUTPUT ${outfile}
+        COMMAND ${QT_MOC_EXECUTABLE}
+        ARGS ${infile} "${moc_flags}" -o ${outfile}
+        MAIN_DEPENDENCY ${infile} VERBATIM)
+      set(${outfiles} ${${outfiles}} ${outfile})
+    endforeach ()
+  endmacro ()
+  ##
+  ## End of "Qt4Macros.cmake" adaptation.
+  ##
+
 else()
-  message("Qt5 has not been found, trying with Qt4")
-  find_package(Qt4 REQUIRED QtGui)
-  link_libraries(
-    Qt4::QtGui
-    )
+  # Not using Linux Standard Base
+  # Find the QtWidgets library
+  find_package(Qt5Widgets QUIET)
+
+  if (Qt5Widgets_FOUND)
+    message("Qt5 has been detected")
+    find_package(Qt5Core REQUIRED)
+    link_libraries(
+      Qt5::Widgets
+      Qt5::Core
+      )
 
-  # Create aliases for the CMake commands
-  macro(ORTHANC_QT_WRAP_UI)
-    QT4_WRAP_UI(${ARGN})
-  endmacro()
-  
-  macro(ORTHANC_QT_WRAP_CPP)
-    QT4_WRAP_CPP(${ARGN})
-  endmacro()
-  
+    if (ENABLE_OPENGL)
+      find_package(Qt5OpenGL REQUIRED)
+      link_libraries(
+        Qt5::OpenGL
+        )
+    endif()
+    
+    # Create aliases for the CMake commands
+    macro(ORTHANC_QT_WRAP_UI)
+      QT5_WRAP_UI(${ARGN})
+    endmacro()
+    
+    macro(ORTHANC_QT_WRAP_CPP)
+      QT5_WRAP_CPP(${ARGN})
+    endmacro()
+
+  else()
+    message("Qt5 has not been found, trying with Qt4")
+    find_package(Qt4 REQUIRED QtGui)
+    link_libraries(
+      Qt4::QtGui
+      )
+
+    if (ENABLE_OPENGL)
+      find_package(Qt4 REQUIRED QtOpenGL)
+      link_libraries(
+        Qt4::QtOpenGL
+        )
+    endif()
+    
+    # Create aliases for the CMake commands
+    macro(ORTHANC_QT_WRAP_UI)
+      QT4_WRAP_UI(${ARGN})
+    endmacro()
+    
+    macro(ORTHANC_QT_WRAP_CPP)
+      QT4_WRAP_CPP(${ARGN})
+    endmacro()  
+  endif()
 endif()
 
 
-list(APPEND QT_SOURCES
-  ${ORTHANC_STONE_ROOT}/Applications/Qt/QCairoWidget.cpp
-  )
-
-ORTHANC_QT_WRAP_CPP(QT_SOURCES
-  ${ORTHANC_STONE_ROOT}/Applications/Qt/QCairoWidget.h
-  )
-
 if (ENABLE_STONE_DEPRECATED)
   list(APPEND QT_SOURCES
+    ${ORTHANC_STONE_ROOT}/Applications/Qt/QCairoWidget.cpp
+    ${ORTHANC_STONE_ROOT}/Applications/Qt/QStoneMainWindow.cpp
     ${ORTHANC_STONE_ROOT}/Applications/Qt/QtStoneApplicationRunner.cpp
-    ${ORTHANC_STONE_ROOT}/Applications/Qt/QStoneMainWindow.cpp
     )
 
   ORTHANC_QT_WRAP_CPP(QT_SOURCES
+    ${ORTHANC_STONE_ROOT}/Applications/Qt/QCairoWidget.h
     ${ORTHANC_STONE_ROOT}/Applications/Qt/QStoneMainWindow.h
     )
 endif()
--- a/Samples/Sdl/FusionMprSdl.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ b/Samples/Sdl/FusionMprSdl.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -20,7 +20,7 @@
 
 #include "FusionMprSdl.h"
 
-#include "../../Applications/Sdl/SdlOpenGLContext.h"
+#include "../../Framework/OpenGL/SdlOpenGLContext.h"
 
 #include "../../Framework/StoneInitialization.h"
 
--- a/Samples/Sdl/TrackerSample.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ b/Samples/Sdl/TrackerSample.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -21,7 +21,7 @@
 #include "TrackerSampleApp.h"
 
  // From Stone
-#include "../../Applications/Sdl/SdlOpenGLContext.h"
+#include "../../Framework/OpenGL/SdlOpenGLContext.h"
 #include "../../Framework/Scene2D/CairoCompositor.h"
 #include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
 #include "../../Framework/Scene2D/OpenGLCompositor.h"
--- a/Samples/Sdl/TrackerSampleApp.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ b/Samples/Sdl/TrackerSampleApp.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -20,8 +20,7 @@
 
 #include "TrackerSampleApp.h"
 
-#include "../../Applications/Sdl/SdlOpenGLContext.h"
-
+#include "../../Framework/OpenGL/SdlOpenGLContext.h"
 #include "../../Framework/Scene2D/CairoCompositor.h"
 #include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
 #include "../../Framework/Scene2D/OpenGLCompositor.h"
--- a/UnitTestsSources/TestExceptions.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 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 "gtest/gtest.h"
-
-#include "../Framework/StoneException.h"
-
-
-
-TEST(StoneExceptions, OrthancToStoneConversion)
-{
-  bool hasBeenCatched = false;
-  try {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-  }
-  catch (Orthanc::OrthancException& orthancException)
-  {
-    hasBeenCatched = true;
-    OrthancStone::StoneOrthancException stoneException(orthancException);
-    ASSERT_EQ(OrthancStone::ErrorCode_OrthancError, stoneException.GetErrorCode());
-    ASSERT_EQ(Orthanc::ErrorCode_InternalError, stoneException.GetOrthancErrorCode());
-  }
-
-  ASSERT_TRUE(hasBeenCatched);
-}
--- a/UnitTestsSources/TestMessageBroker.cpp	Tue Oct 15 10:53:28 2019 +0200
+++ b/UnitTestsSources/TestMessageBroker.cpp	Tue Oct 15 10:54:53 2019 +0200
@@ -22,7 +22,6 @@
 #include "gtest/gtest.h"
 
 #include "Framework/Messages/MessageBroker.h"
-#include "Framework/Messages/Promise.h"
 #include "Framework/Messages/IObservable.h"
 #include "Framework/Messages/IObserver.h"
 #include "Framework/Messages/MessageForwarder.h"
@@ -82,67 +81,6 @@
       observedObject_.RegisterObserverCallback(new MessageForwarder<MyObservable::MyCustomMessage>(broker, *this));
     }
   };
-
-
-  class MyPromiseSource : public IObservable
-  {
-    Promise* currentPromise_;
-
-  public:
-    struct MyPromiseMessage: public IMessage
-    {
-      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
-
-      int increment;
-
-      MyPromiseMessage(int increment) :
-        increment(increment)
-      {
-      }
-    };
-
-    MyPromiseSource(MessageBroker& broker)
-      : IObservable(broker),
-        currentPromise_(NULL)
-    {}
-
-    Promise& StartSomethingAsync()
-    {
-      currentPromise_ = new Promise(GetBroker());
-      return *currentPromise_;
-    }
-
-    void CompleteSomethingAsyncWithSuccess(int payload)
-    {
-      currentPromise_->Success(MyPromiseMessage(payload));
-      delete currentPromise_;
-    }
-
-    void CompleteSomethingAsyncWithFailure(int payload)
-    {
-      currentPromise_->Failure(MyPromiseMessage(payload));
-      delete currentPromise_;
-    }
-  };
-
-
-  class MyPromiseTarget : public IObserver
-  {
-  public:
-    MyPromiseTarget(MessageBroker& broker)
-      : IObserver(broker)
-    {}
-
-    void IncrementCounter(const MyPromiseSource::MyPromiseMessage& args)
-    {
-      testCounter += args.increment;
-    }
-
-    void DecrementCounter(const MyPromiseSource::MyPromiseMessage& args)
-    {
-      testCounter -= args.increment;
-    }
-  };
 }
 
 
@@ -254,60 +192,6 @@
 }
 
 
-TEST(MessageBroker, TestPromiseSuccessFailure)
-{
-  MessageBroker broker;
-  MyPromiseSource  source(broker);
-  MyPromiseTarget target(broker);
-
-  // test a successful promise
-  source.StartSomethingAsync()
-      .Then(new Callable<MyPromiseTarget, MyPromiseSource::MyPromiseMessage>(target, &MyPromiseTarget::IncrementCounter))
-      .Else(new Callable<MyPromiseTarget, MyPromiseSource::MyPromiseMessage>(target, &MyPromiseTarget::DecrementCounter));
-
-  testCounter = 0;
-  source.CompleteSomethingAsyncWithSuccess(10);
-  ASSERT_EQ(10, testCounter);
-
-  // test a failing promise
-  source.StartSomethingAsync()
-      .Then(new Callable<MyPromiseTarget, MyPromiseSource::MyPromiseMessage>(target, &MyPromiseTarget::IncrementCounter))
-      .Else(new Callable<MyPromiseTarget, MyPromiseSource::MyPromiseMessage>(target, &MyPromiseTarget::DecrementCounter));
-
-  testCounter = 0;
-  source.CompleteSomethingAsyncWithFailure(15);
-  ASSERT_EQ(-15, testCounter);
-}
-
-TEST(MessageBroker, TestPromiseDeleteTarget)
-{
-  MessageBroker broker;
-  MyPromiseSource source(broker);
-  MyPromiseTarget* target = new MyPromiseTarget(broker);
-
-  // create the promise
-  source.StartSomethingAsync()
-      .Then(new Callable<MyPromiseTarget, MyPromiseSource::MyPromiseMessage>(*target, &MyPromiseTarget::IncrementCounter))
-      .Else(new Callable<MyPromiseTarget, MyPromiseSource::MyPromiseMessage>(*target, &MyPromiseTarget::DecrementCounter));
-
-  // delete the promise target
-  delete target;
-
-  // trigger the promise, make sure it does not throw and does not call the callback
-  testCounter = 0;
-  source.CompleteSomethingAsyncWithSuccess(10);
-  ASSERT_EQ(0, testCounter);
-
-  // test a failing promise
-  source.StartSomethingAsync()
-      .Then(new Callable<MyPromiseTarget, MyPromiseSource::MyPromiseMessage>(*target, &MyPromiseTarget::IncrementCounter))
-      .Else(new Callable<MyPromiseTarget, MyPromiseSource::MyPromiseMessage>(*target, &MyPromiseTarget::DecrementCounter));
-
-  testCounter = 0;
-  source.CompleteSomethingAsyncWithFailure(15);
-  ASSERT_EQ(0, testCounter);
-}
-
 #if 0 /* __cplusplus >= 201103L*/
 
 TEST(MessageBroker, TestLambdaSimpleUseCase)