changeset 1203:f3bb9a6dd949 broker

locking abstraction in IViewport
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 29 Nov 2019 21:22:21 +0100
parents 54cbffabdc45
children b519c1c878f1
files Framework/Scene2DViewport/AngleMeasureTool.cpp Framework/Scene2DViewport/CreateAngleMeasureTracker.cpp Framework/Scene2DViewport/CreateLineMeasureTracker.cpp Framework/Scene2DViewport/EditAngleMeasureTracker.cpp Framework/Scene2DViewport/EditLineMeasureTracker.cpp Framework/Scene2DViewport/LayerHolder.cpp Framework/Scene2DViewport/LayerHolder.h Framework/Scene2DViewport/LineMeasureTool.cpp Framework/Scene2DViewport/MeasureToolsToolbox.cpp Framework/Scene2DViewport/MeasureTrackers.cpp Framework/Scene2DViewport/MeasureTrackers.h Framework/Scene2DViewport/ViewportController.cpp Framework/Scene2DViewport/ViewportController.h Framework/Viewport/IViewport.h Framework/Viewport/SdlViewport.cpp Framework/Viewport/SdlViewport.h Framework/Viewport/ViewportBase.cpp Framework/Viewport/ViewportBase.h
diffstat 18 files changed, 543 insertions(+), 437 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/Scene2DViewport/AngleMeasureTool.cpp	Fri Nov 29 11:03:41 2019 +0100
+++ b/Framework/Scene2DViewport/AngleMeasureTool.cpp	Fri Nov 29 21:22:21 2019 +0100
@@ -131,8 +131,10 @@
 
   AngleMeasureTool::AngleHighlightArea AngleMeasureTool::AngleHitTest(ScenePoint2D p) const
   {
+    std::auto_ptr<IViewport::ILock> lock(GetController()->GetViewport().Lock());
+    
     const double pixelToScene =
-      GetController()->GetScene().GetCanvasToSceneTransform().ComputeZoom();
+      lock->GetScene().GetCanvasToSceneTransform().ComputeZoom();
     const double SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD = pixelToScene * HIT_TEST_MAX_DISTANCE_CANVAS_COORD * pixelToScene * HIT_TEST_MAX_DISTANCE_CANVAS_COORD;
 
     {
@@ -176,8 +178,10 @@
 
   boost::shared_ptr<IFlexiblePointerTracker> AngleMeasureTool::CreateEditionTracker(const PointerEvent& e)
   {
+    std::auto_ptr<IViewport::ILock> lock(GetController()->GetViewport().Lock());
+    
     ScenePoint2D scenePos = e.GetMainPosition().Apply(
-      GetController()->GetScene().GetCanvasToSceneTransform());
+      lock->GetScene().GetCanvasToSceneTransform());
 
     if (!HitTest(scenePos))
       return boost::shared_ptr<IFlexiblePointerTracker>();
@@ -208,77 +212,82 @@
       boost::shared_ptr<ViewportController> controller = GetController();
       if (IsEnabled())
       {
+        std::auto_ptr<IViewport::ILock> lock(GetController()->GetViewport().Lock());
+    
         layerHolder_->CreateLayersIfNeeded();
 
         {
           // Fill the polyline layer with the measurement lines
           PolylineSceneLayer* polylineLayer = layerHolder_->GetPolylineLayer(0);
-          polylineLayer->ClearAllChains();
+          if (polylineLayer)
+          {
+            polylineLayer->ClearAllChains();
+
+            const Color color(TOOL_ANGLE_LINES_COLOR_RED, TOOL_ANGLE_LINES_COLOR_GREEN, TOOL_ANGLE_LINES_COLOR_BLUE);
+            const Color highlightColor(TOOL_ANGLE_LINES_HL_COLOR_RED, TOOL_ANGLE_LINES_HL_COLOR_GREEN, TOOL_ANGLE_LINES_HL_COLOR_BLUE);
+
+            // sides
+            {
+              {
+                PolylineSceneLayer::Chain chain;
+                chain.push_back(side1End_);
+                chain.push_back(center_);
 
-          const Color color(TOOL_ANGLE_LINES_COLOR_RED, TOOL_ANGLE_LINES_COLOR_GREEN, TOOL_ANGLE_LINES_COLOR_BLUE);
-          const Color highlightColor(TOOL_ANGLE_LINES_HL_COLOR_RED, TOOL_ANGLE_LINES_HL_COLOR_GREEN, TOOL_ANGLE_LINES_HL_COLOR_BLUE);
+                if ((angleHighlightArea_ == AngleHighlightArea_Side1) || (angleHighlightArea_ == AngleHighlightArea_Side2))
+                  polylineLayer->AddChain(chain, false, highlightColor);
+                else
+                  polylineLayer->AddChain(chain, false, color);
+              }
+              {
+                PolylineSceneLayer::Chain chain;
+                chain.push_back(side2End_);
+                chain.push_back(center_);
+                if ((angleHighlightArea_ == AngleHighlightArea_Side1) || (angleHighlightArea_ == AngleHighlightArea_Side2))
+                  polylineLayer->AddChain(chain, false, highlightColor);
+                else
+                  polylineLayer->AddChain(chain, false, color);
+              }
+            }
 
-          // sides
-          {
+            // Create the handles
+            {
+              {
+                PolylineSceneLayer::Chain chain;
+                //TODO: take DPI into account
+                AddSquare(chain, lock->GetScene(), side1End_, 
+                          GetController()->GetHandleSideLengthS());
+              
+                if (angleHighlightArea_ == AngleHighlightArea_Side1End)
+                  polylineLayer->AddChain(chain, true, highlightColor);
+                else
+                  polylineLayer->AddChain(chain, true, color);
+              
+              }
+              {
+                PolylineSceneLayer::Chain chain;
+                //TODO: take DPI into account
+                AddSquare(chain, lock->GetScene(), side2End_, 
+                          GetController()->GetHandleSideLengthS());
+
+                if (angleHighlightArea_ == AngleHighlightArea_Side2End)
+                  polylineLayer->AddChain(chain, true, highlightColor);
+                else
+                  polylineLayer->AddChain(chain, true, color);
+              }
+            }
+
+            // Create the arc
             {
               PolylineSceneLayer::Chain chain;
-              chain.push_back(side1End_);
-              chain.push_back(center_);
 
-              if ((angleHighlightArea_ == AngleHighlightArea_Side1) || (angleHighlightArea_ == AngleHighlightArea_Side2))
-                polylineLayer->AddChain(chain, false, highlightColor);
-              else
-                polylineLayer->AddChain(chain, false, color);
-            }
-            {
-              PolylineSceneLayer::Chain chain;
-              chain.push_back(side2End_);
-              chain.push_back(center_);
-              if ((angleHighlightArea_ == AngleHighlightArea_Side1) || (angleHighlightArea_ == AngleHighlightArea_Side2))
+              AddShortestArc(chain, side1End_, center_, side2End_,
+                             controller->GetAngleToolArcRadiusS());
+              if (angleHighlightArea_ == AngleHighlightArea_Center)
                 polylineLayer->AddChain(chain, false, highlightColor);
               else
                 polylineLayer->AddChain(chain, false, color);
             }
           }
-
-          // Create the handles
-          {
-            {
-              PolylineSceneLayer::Chain chain;
-              //TODO: take DPI into account
-              AddSquare(chain, GetController()->GetScene(), side1End_, 
-                GetController()->GetHandleSideLengthS());
-              
-              if (angleHighlightArea_ == AngleHighlightArea_Side1End)
-                polylineLayer->AddChain(chain, true, highlightColor);
-              else
-                polylineLayer->AddChain(chain, true, color);
-              
-            }
-            {
-              PolylineSceneLayer::Chain chain;
-              //TODO: take DPI into account
-              AddSquare(chain, GetController()->GetScene(), side2End_, 
-                GetController()->GetHandleSideLengthS());
-
-              if (angleHighlightArea_ == AngleHighlightArea_Side2End)
-                  polylineLayer->AddChain(chain, true, highlightColor);
-              else
-                polylineLayer->AddChain(chain, true, color);
-            }
-          }
-
-          // Create the arc
-          {
-            PolylineSceneLayer::Chain chain;
-
-            AddShortestArc(chain, side1End_, center_, side2End_,
-                           controller->GetAngleToolArcRadiusS());
-            if (angleHighlightArea_ == AngleHighlightArea_Center)
-              polylineLayer->AddChain(chain, false, highlightColor);
-            else
-              polylineLayer->AddChain(chain, false, color);
-          }
         }
         {
           // Set the text layer
@@ -308,10 +317,10 @@
 
 #if ORTHANC_STONE_ENABLE_OUTLINED_TEXT == 1
           SetTextLayerOutlineProperties(
-            GetController()->GetScene(), layerHolder_, buf, ScenePoint2D(pointX, pointY), 0);
+            lock->GetScene(), layerHolder_, buf, ScenePoint2D(pointX, pointY), 0);
 #else
           SetTextLayerProperties(
-            GetController()->GetScene(), layerHolder_, buf, ScenePoint2D(pointX, pointY) , 0);
+            lock->GetScene(), layerHolder_, buf, ScenePoint2D(pointX, pointY) , 0);
 #endif
 
 #if 0
--- a/Framework/Scene2DViewport/CreateAngleMeasureTracker.cpp	Fri Nov 29 11:03:41 2019 +0100
+++ b/Framework/Scene2DViewport/CreateAngleMeasureTracker.cpp	Fri Nov 29 21:22:21 2019 +0100
@@ -31,10 +31,18 @@
     : CreateMeasureTracker(controllerW)
     , state_(CreatingSide1)
   {
-    command_.reset(
-      new CreateAngleMeasureCommand(
-        controllerW,
-        e.GetMainPosition().Apply(GetScene().GetCanvasToSceneTransform())));
+    ScenePoint2D point = e.GetMainPosition();
+    
+    {
+      boost::shared_ptr<ViewportController> controller = controllerW.lock();
+      if (controller)
+      {
+        std::auto_ptr<IViewport::ILock> lock(controller->GetViewport().Lock());
+        point = e.GetMainPosition().Apply(lock->GetScene().GetCanvasToSceneTransform());
+      }
+    }
+    
+    command_.reset(new CreateAngleMeasureCommand(controllerW, point));
   }
 
   CreateAngleMeasureTracker::~CreateAngleMeasureTracker()
@@ -50,24 +58,29 @@
         "PointerMove: active_ == false");
     }
 
-    ScenePoint2D scenePos = event.GetMainPosition().Apply(
-      GetScene().GetCanvasToSceneTransform());
-
-    switch (state_)
+    boost::shared_ptr<ViewportController> controller = controllerW_.lock();
+    if (controller)
     {
-    case CreatingSide1:
-      GetCommand()->SetCenter(scenePos);
-      break;
-    case CreatingSide2:
-      GetCommand()->SetSide2End(scenePos);
-      break;
-    default:
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
-        "Wrong state in CreateAngleMeasureTracker::"
-        "PointerMove: state_ invalid");
+      std::auto_ptr<IViewport::ILock> lock(controller->GetViewport().Lock());
+      ScenePoint2D scenePos = event.GetMainPosition().Apply(
+        lock->GetScene().GetCanvasToSceneTransform());
+
+      switch (state_)
+      {
+        case CreatingSide1:
+          GetCommand()->SetCenter(scenePos);
+          break;
+        case CreatingSide2:
+          GetCommand()->SetSide2End(scenePos);
+          break;
+        default:
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
+                                          "Wrong state in CreateAngleMeasureTracker::"
+                                          "PointerMove: state_ invalid");
+      }
+      //LOG(TRACE) << "scenePos.GetX() = " << scenePos.GetX() << "     " <<
+      //  "scenePos.GetY() = " << scenePos.GetY();
     }
-    //LOG(TRACE) << "scenePos.GetX() = " << scenePos.GetX() << "     " <<
-    //  "scenePos.GetY() = " << scenePos.GetY();
   }
 
   void CreateAngleMeasureTracker::PointerUp(const PointerEvent& e)
--- a/Framework/Scene2DViewport/CreateLineMeasureTracker.cpp	Fri Nov 29 11:03:41 2019 +0100
+++ b/Framework/Scene2DViewport/CreateLineMeasureTracker.cpp	Fri Nov 29 21:22:21 2019 +0100
@@ -30,10 +30,18 @@
     const PointerEvent&             e)
     : CreateMeasureTracker(controllerW)
   {
-    command_.reset(
-      new CreateLineMeasureCommand(
-        controllerW,
-        e.GetMainPosition().Apply(GetScene().GetCanvasToSceneTransform())));
+    ScenePoint2D point = e.GetMainPosition();
+    
+    {
+      boost::shared_ptr<ViewportController> controller = controllerW.lock();
+      if (controller)
+      {
+        std::auto_ptr<IViewport::ILock> lock(controller->GetViewport().Lock());
+        point = e.GetMainPosition().Apply(lock->GetScene().GetCanvasToSceneTransform());
+      }
+    }
+
+    command_.reset(new CreateLineMeasureCommand(controllerW, point));
   }
 
   CreateLineMeasureTracker::~CreateLineMeasureTracker()
@@ -50,16 +58,21 @@
         "PointerMove: active_ == false");
     }
 
-    ScenePoint2D scenePos = event.GetMainPosition().Apply(
-      GetScene().GetCanvasToSceneTransform());
-
-    //LOG(TRACE) << "scenePos.GetX() = " << scenePos.GetX() << "     " <<
-    //  "scenePos.GetY() = " << scenePos.GetY();
-
-    CreateLineMeasureTracker* concreteThis =
-      dynamic_cast<CreateLineMeasureTracker*>(this);
-    assert(concreteThis != NULL);
-    GetCommand()->SetEnd(scenePos);
+    boost::shared_ptr<ViewportController> controller = controllerW_.lock();
+    if (controller)
+    {
+      std::auto_ptr<IViewport::ILock> lock(controller->GetViewport().Lock());
+      ScenePoint2D scenePos = event.GetMainPosition().Apply(
+        lock->GetScene().GetCanvasToSceneTransform());
+      
+      //LOG(TRACE) << "scenePos.GetX() = " << scenePos.GetX() << "     " <<
+      //  "scenePos.GetY() = " << scenePos.GetY();
+      
+      CreateLineMeasureTracker* concreteThis =
+        dynamic_cast<CreateLineMeasureTracker*>(this);
+      assert(concreteThis != NULL);
+      GetCommand()->SetEnd(scenePos);
+    }
   }
 
   void CreateLineMeasureTracker::PointerUp(const PointerEvent& e)
--- a/Framework/Scene2DViewport/EditAngleMeasureTracker.cpp	Fri Nov 29 11:03:41 2019 +0100
+++ b/Framework/Scene2DViewport/EditAngleMeasureTracker.cpp	Fri Nov 29 21:22:21 2019 +0100
@@ -31,8 +31,14 @@
     const PointerEvent& e)
     : EditMeasureTracker(controllerW, e)
   {
-    ScenePoint2D scenePos = e.GetMainPosition().Apply(
-      GetScene().GetCanvasToSceneTransform());
+    ScenePoint2D scenePos = e.GetMainPosition();
+
+    boost::shared_ptr<ViewportController> controller = controllerW_.lock();
+    if (controller)
+    {
+      std::auto_ptr<IViewport::ILock> lock(controller->GetViewport().Lock());
+      scenePos = e.GetMainPosition().Apply(lock->GetScene().GetCanvasToSceneTransform());
+    }
 
     modifiedZone_ = dynamic_cast<AngleMeasureTool&>(*measureTool).AngleHitTest(scenePos);
 
@@ -46,50 +52,55 @@
 
   void EditAngleMeasureTracker::PointerMove(const PointerEvent& e)
   {
-    ScenePoint2D scenePos = e.GetMainPosition().Apply(
-      GetScene().GetCanvasToSceneTransform());
-
-    ScenePoint2D delta = scenePos - GetOriginalClickPosition();
-
-    boost::shared_ptr<AngleMeasureToolMemento> memento =
-      boost::dynamic_pointer_cast<AngleMeasureToolMemento>(command_->mementoOriginal_);
-
-    ORTHANC_ASSERT(memento.get() != NULL);
-
-    switch (modifiedZone_)
-    {
-    case AngleMeasureTool::AngleHighlightArea_Center:
-    {
-      ScenePoint2D newCenter = memento->center_ + delta;
-      GetCommand()->SetCenter(newCenter);
-    }
-    break;
-    case AngleMeasureTool::AngleHighlightArea_Side1:
-    case AngleMeasureTool::AngleHighlightArea_Side2:
+    boost::shared_ptr<ViewportController> controller = controllerW_.lock();
+    if (controller)
     {
-      ScenePoint2D newCenter = memento->center_ + delta;
-      ScenePoint2D newSide1End = memento->side1End_ + delta;
-      ScenePoint2D newSide2End = memento->side2End_ + delta;
-      GetCommand()->SetCenter(newCenter);
-      GetCommand()->SetSide1End(newSide1End);
-      GetCommand()->SetSide2End(newSide2End);
-    }
-    break;
-    case AngleMeasureTool::AngleHighlightArea_Side1End:
-    {
-      ScenePoint2D newSide1End = memento->side1End_ + delta;
-      GetCommand()->SetSide1End(newSide1End);
-    }
-    break;
-    case AngleMeasureTool::AngleHighlightArea_Side2End:
-    {
-      ScenePoint2D newSide2End = memento->side2End_ + delta;
-      GetCommand()->SetSide2End(newSide2End);
-    }
-    break;
-    default:
-      LOG(WARNING) << "Warning: please retry the measuring tool editing operation!";
-      break;
+      std::auto_ptr<IViewport::ILock> lock(controller->GetViewport().Lock());
+      ScenePoint2D scenePos = e.GetMainPosition().Apply(
+        lock->GetScene().GetCanvasToSceneTransform());
+
+      ScenePoint2D delta = scenePos - GetOriginalClickPosition();
+
+      boost::shared_ptr<AngleMeasureToolMemento> memento =
+        boost::dynamic_pointer_cast<AngleMeasureToolMemento>(command_->mementoOriginal_);
+
+      ORTHANC_ASSERT(memento.get() != NULL);
+
+      switch (modifiedZone_)
+      {
+        case AngleMeasureTool::AngleHighlightArea_Center:
+        {
+          ScenePoint2D newCenter = memento->center_ + delta;
+          GetCommand()->SetCenter(newCenter);
+        }
+        break;
+        case AngleMeasureTool::AngleHighlightArea_Side1:
+        case AngleMeasureTool::AngleHighlightArea_Side2:
+        {
+          ScenePoint2D newCenter = memento->center_ + delta;
+          ScenePoint2D newSide1End = memento->side1End_ + delta;
+          ScenePoint2D newSide2End = memento->side2End_ + delta;
+          GetCommand()->SetCenter(newCenter);
+          GetCommand()->SetSide1End(newSide1End);
+          GetCommand()->SetSide2End(newSide2End);
+        }
+        break;
+        case AngleMeasureTool::AngleHighlightArea_Side1End:
+        {
+          ScenePoint2D newSide1End = memento->side1End_ + delta;
+          GetCommand()->SetSide1End(newSide1End);
+        }
+        break;
+        case AngleMeasureTool::AngleHighlightArea_Side2End:
+        {
+          ScenePoint2D newSide2End = memento->side2End_ + delta;
+          GetCommand()->SetSide2End(newSide2End);
+        }
+        break;
+        default:
+          LOG(WARNING) << "Warning: please retry the measuring tool editing operation!";
+          break;
+      }
     }
   }
 
--- a/Framework/Scene2DViewport/EditLineMeasureTracker.cpp	Fri Nov 29 11:03:41 2019 +0100
+++ b/Framework/Scene2DViewport/EditLineMeasureTracker.cpp	Fri Nov 29 21:22:21 2019 +0100
@@ -32,8 +32,16 @@
     const PointerEvent& e) 
     : EditMeasureTracker(controllerW, e)
   {
-    ScenePoint2D scenePos = e.GetMainPosition().Apply(
-      GetScene().GetCanvasToSceneTransform());
+    ScenePoint2D scenePos = e.GetMainPosition();
+
+    {
+      boost::shared_ptr<ViewportController> controller = controllerW.lock();
+      if (controller)
+      {
+        std::auto_ptr<IViewport::ILock> lock(controller->GetViewport().Lock());
+        scenePos = e.GetMainPosition().Apply(lock->GetScene().GetCanvasToSceneTransform());
+      }
+    }
 
     modifiedZone_ = dynamic_cast<LineMeasureTool&>(*measureTool).LineHitTest(scenePos);
 
@@ -47,44 +55,49 @@
 
   void EditLineMeasureTracker::PointerMove(const PointerEvent& e)
   {
-    ScenePoint2D scenePos = e.GetMainPosition().Apply(
-      GetScene().GetCanvasToSceneTransform());
-
-    ScenePoint2D delta = scenePos - GetOriginalClickPosition();
-
-    boost::shared_ptr<LineMeasureToolMemento> memento =
-      boost::dynamic_pointer_cast<LineMeasureToolMemento>(command_->mementoOriginal_);
-
-    ORTHANC_ASSERT(memento.get() != NULL);
-
-    switch (modifiedZone_)
-    {
-    case LineMeasureTool::LineHighlightArea_Start:
+    boost::shared_ptr<ViewportController> controller = controllerW_.lock();
+    if (controller)
     {
-      ScenePoint2D newStart = memento->start_ + delta;
-      GetCommand()->SetStart(newStart);
-    }
-    break;
-    case LineMeasureTool::LineHighlightArea_End:
-    {
-      ScenePoint2D newEnd = memento->end_ + delta;
-      GetCommand()->SetEnd(newEnd);
-    }
-    break;
-    case LineMeasureTool::LineHighlightArea_Segment:
-    {
-      ScenePoint2D newStart = memento->start_ + delta;
-      ScenePoint2D newEnd = memento->end_ + delta;
-      GetCommand()->SetStart(newStart);
-      GetCommand()->SetEnd(newEnd);
-    }
-    break;
-    default:
-      LOG(WARNING) << "Warning: please retry the measuring tool editing operation!";
+      std::auto_ptr<IViewport::ILock> lock(controller->GetViewport().Lock());
+      ScenePoint2D scenePos = e.GetMainPosition().Apply(
+        lock->GetScene().GetCanvasToSceneTransform());
+
+      ScenePoint2D delta = scenePos - GetOriginalClickPosition();
+
+      boost::shared_ptr<LineMeasureToolMemento> memento =
+        boost::dynamic_pointer_cast<LineMeasureToolMemento>(command_->mementoOriginal_);
+
+      ORTHANC_ASSERT(memento.get() != NULL);
+
+      switch (modifiedZone_)
+      {
+        case LineMeasureTool::LineHighlightArea_Start:
+        {
+          ScenePoint2D newStart = memento->start_ + delta;
+          GetCommand()->SetStart(newStart);
+        }
         break;
+        case LineMeasureTool::LineHighlightArea_End:
+        {
+          ScenePoint2D newEnd = memento->end_ + delta;
+          GetCommand()->SetEnd(newEnd);
+        }
+        break;
+        case LineMeasureTool::LineHighlightArea_Segment:
+        {
+          ScenePoint2D newStart = memento->start_ + delta;
+          ScenePoint2D newEnd = memento->end_ + delta;
+          GetCommand()->SetStart(newStart);
+          GetCommand()->SetEnd(newEnd);
+        }
+        break;
+        default:
+          LOG(WARNING) << "Warning: please retry the measuring tool editing operation!";
+          break;
+      }
     }
   }
-
+  
   void EditLineMeasureTracker::PointerUp(const PointerEvent& e)
   {
     alive_ = false;
--- a/Framework/Scene2DViewport/LayerHolder.cpp	Fri Nov 29 11:03:41 2019 +0100
+++ b/Framework/Scene2DViewport/LayerHolder.cpp	Fri Nov 29 21:22:21 2019 +0100
@@ -43,24 +43,30 @@
 
   void LayerHolder::CreateLayers()
   {
-    assert(baseLayerIndex_ == -1);
+    boost::shared_ptr<ViewportController> controller = controllerW_.lock();
 
-    baseLayerIndex_ = GetScene().GetMaxDepth() + 100;
+    if (controller)
+    {
+      std::auto_ptr<IViewport::ILock> lock(controller->GetViewport().Lock());
+    
+      assert(baseLayerIndex_ == -1);
+
+      baseLayerIndex_ = lock->GetScene().GetMaxDepth() + 100;
 
-    for (int i = 0; i < polylineLayerCount_; ++i)
-    {
-      std::auto_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer());
-      GetScene().SetLayer(baseLayerIndex_ + i, layer.release());
+      for (int i = 0; i < polylineLayerCount_; ++i)
+      {
+        std::auto_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer());
+        lock->GetScene().SetLayer(baseLayerIndex_ + i, layer.release());
+      }
+
+      for (int i = 0; i < textLayerCount_; ++i)
+      {
+        std::auto_ptr<TextSceneLayer> layer(new TextSceneLayer());
+        lock->GetScene().SetLayer(
+          baseLayerIndex_ + polylineLayerCount_ + i,
+          layer.release());
+      }
     }
-
-    for (int i = 0; i < textLayerCount_; ++i)
-    {
-      std::auto_ptr<TextSceneLayer> layer(new TextSceneLayer());
-      GetScene().SetLayer(
-        baseLayerIndex_ + polylineLayerCount_ + i,
-        layer.release());
-    }
-
   }
 
   void LayerHolder::CreateLayersIfNeeded()
@@ -74,13 +80,6 @@
     return (baseLayerIndex_ != -1);
   }
 
-  Scene2D& LayerHolder::GetScene()
-  {
-    boost::shared_ptr<ViewportController> controller = controllerW_.lock();
-    ORTHANC_ASSERT(controller.get() != 0, "Zombie attack!");
-    return controller->GetScene();
-  }
-
   void LayerHolder::DeleteLayersIfNeeded()
   {
     if (baseLayerIndex_ != -1)
@@ -89,42 +88,71 @@
   
   void LayerHolder::DeleteLayers()
   {
-    for (int i = 0; i < textLayerCount_ + polylineLayerCount_; ++i)
+    boost::shared_ptr<ViewportController> controller = controllerW_.lock();
+
+    if (controller)
     {
-      ORTHANC_ASSERT(GetScene().HasLayer(baseLayerIndex_ + i), "No layer");
-      GetScene().DeleteLayer(baseLayerIndex_ + i);
+      std::auto_ptr<IViewport::ILock> lock(controller->GetViewport().Lock());
+    
+      for (int i = 0; i < textLayerCount_ + polylineLayerCount_; ++i)
+      {
+        ORTHANC_ASSERT(lock->GetScene().HasLayer(baseLayerIndex_ + i), "No layer");
+        lock->GetScene().DeleteLayer(baseLayerIndex_ + i);
+      }
+      baseLayerIndex_ = -1;
     }
-    baseLayerIndex_ = -1;
   }
-
+  
   PolylineSceneLayer* LayerHolder::GetPolylineLayer(int index /*= 0*/)
   {
-    using namespace Orthanc;
-    ORTHANC_ASSERT(baseLayerIndex_ != -1);
-    ORTHANC_ASSERT(GetScene().HasLayer(GetPolylineLayerIndex(index)));
-    ISceneLayer* layer =
-      &(GetScene().GetLayer(GetPolylineLayerIndex(index)));
+    boost::shared_ptr<ViewportController> controller = controllerW_.lock();
+
+    if (controller)
+    {
+      std::auto_ptr<IViewport::ILock> lock(controller->GetViewport().Lock());
 
-    PolylineSceneLayer* concreteLayer =
-      dynamic_cast<PolylineSceneLayer*>(layer);
-
-    ORTHANC_ASSERT(concreteLayer != NULL);
-    return concreteLayer;
+      using namespace Orthanc;
+      ORTHANC_ASSERT(baseLayerIndex_ != -1);
+      ORTHANC_ASSERT(lock->GetScene().HasLayer(GetPolylineLayerIndex(index)));
+      ISceneLayer* layer =
+        &(lock->GetScene().GetLayer(GetPolylineLayerIndex(index)));
+      
+      PolylineSceneLayer* concreteLayer =
+        dynamic_cast<PolylineSceneLayer*>(layer);
+      
+      ORTHANC_ASSERT(concreteLayer != NULL);
+      return concreteLayer;
+    }
+    else
+    {
+      return NULL; // TODO
+    }
   }
 
   TextSceneLayer* LayerHolder::GetTextLayer(int index /*= 0*/)
   {
-    using namespace Orthanc;
-    ORTHANC_ASSERT(baseLayerIndex_ != -1);
-    ORTHANC_ASSERT(GetScene().HasLayer(GetTextLayerIndex(index)));
-    ISceneLayer* layer =
-      &(GetScene().GetLayer(GetTextLayerIndex(index)));
+    boost::shared_ptr<ViewportController> controller = controllerW_.lock();
+
+    if (controller)
+    {
+      std::auto_ptr<IViewport::ILock> lock(controller->GetViewport().Lock());
 
-    TextSceneLayer* concreteLayer =
-      dynamic_cast<TextSceneLayer*>(layer);
-
-    ORTHANC_ASSERT(concreteLayer != NULL);
-    return concreteLayer;
+      using namespace Orthanc;
+      ORTHANC_ASSERT(baseLayerIndex_ != -1);
+      ORTHANC_ASSERT(lock->GetScene().HasLayer(GetTextLayerIndex(index)));
+      ISceneLayer* layer =
+        &(lock->GetScene().GetLayer(GetTextLayerIndex(index)));
+      
+      TextSceneLayer* concreteLayer =
+        dynamic_cast<TextSceneLayer*>(layer);
+      
+      ORTHANC_ASSERT(concreteLayer != NULL);
+      return concreteLayer;
+    }
+    else
+    {
+      return NULL; // TODO
+    }
   }
 
   int LayerHolder::GetPolylineLayerIndex(int index /*= 0*/)
--- a/Framework/Scene2DViewport/LayerHolder.h	Fri Nov 29 11:03:41 2019 +0100
+++ b/Framework/Scene2DViewport/LayerHolder.h	Fri Nov 29 21:22:21 2019 +0100
@@ -97,7 +97,6 @@
     int GetPolylineLayerIndex(int index = 0);
     int GetTextLayerIndex(int index = 0);
     int GetInfoTextLayerIndex(int index = 0);
-    Scene2D& GetScene();
 
     int textLayerCount_;
     int polylineLayerCount_;
--- a/Framework/Scene2DViewport/LineMeasureTool.cpp	Fri Nov 29 11:03:41 2019 +0100
+++ b/Framework/Scene2DViewport/LineMeasureTool.cpp	Fri Nov 29 21:22:21 2019 +0100
@@ -108,8 +108,10 @@
 
   LineMeasureTool::LineHighlightArea LineMeasureTool::LineHitTest(ScenePoint2D p) const
   {
+    std::auto_ptr<IViewport::ILock> lock(GetController()->GetViewport().Lock());
+    
     const double pixelToScene =
-      GetController()->GetScene().GetCanvasToSceneTransform().ComputeZoom();
+      lock->GetScene().GetCanvasToSceneTransform().ComputeZoom();
     const double SQUARED_HIT_TEST_MAX_DISTANCE_SCENE_COORD = pixelToScene * HIT_TEST_MAX_DISTANCE_CANVAS_COORD * pixelToScene * HIT_TEST_MAX_DISTANCE_CANVAS_COORD;
 
     const double sqDistanceFromStart = ScenePoint2D::SquaredDistancePtPt(p, start_);
@@ -134,8 +136,10 @@
 
   boost::shared_ptr<IFlexiblePointerTracker> LineMeasureTool::CreateEditionTracker(const PointerEvent& e)
   {
+    std::auto_ptr<IViewport::ILock> lock(GetController()->GetViewport().Lock());
+    
     ScenePoint2D scenePos = e.GetMainPosition().Apply(
-      GetController()->GetScene().GetCanvasToSceneTransform());
+      lock->GetScene().GetCanvasToSceneTransform());
 
     if (!HitTest(scenePos))
       return boost::shared_ptr<IFlexiblePointerTracker>();
@@ -176,61 +180,65 @@
     {
       if (IsEnabled())
       {
+        std::auto_ptr<IViewport::ILock> lock(GetController()->GetViewport().Lock());
+    
         layerHolder_->CreateLayersIfNeeded();
 
         {
           // Fill the polyline layer with the measurement line
 
           PolylineSceneLayer* polylineLayer = layerHolder_->GetPolylineLayer(0);
-          polylineLayer->ClearAllChains();
-
-          const Color color(TOOL_LINES_COLOR_RED, 
-                            TOOL_LINES_COLOR_GREEN, 
-                            TOOL_LINES_COLOR_BLUE);
-
-          const Color highlightColor(TOOL_LINES_HL_COLOR_RED,
-                                     TOOL_LINES_HL_COLOR_GREEN,
-                                     TOOL_LINES_HL_COLOR_BLUE);
-
+          if (polylineLayer)
           {
-            PolylineSceneLayer::Chain chain;
-            chain.push_back(start_);
-            chain.push_back(end_);
-            if(lineHighlightArea_ == LineHighlightArea_Segment)
-              polylineLayer->AddChain(chain, false, highlightColor);
-            else
-              polylineLayer->AddChain(chain, false, color);
-          }
+            polylineLayer->ClearAllChains();
 
-          // handles
-          {
-            {
-              PolylineSceneLayer::Chain chain;
-              
-              //TODO: take DPI into account
-              AddSquare(chain, GetController()->GetScene(), start_, 
-                GetController()->GetHandleSideLengthS());
-              
-              if (lineHighlightArea_ == LineHighlightArea_Start)
-                polylineLayer->AddChain(chain, true, highlightColor);
-              else
-                polylineLayer->AddChain(chain, true, color);
-            }
+            const Color color(TOOL_LINES_COLOR_RED, 
+                              TOOL_LINES_COLOR_GREEN, 
+                              TOOL_LINES_COLOR_BLUE);
+
+            const Color highlightColor(TOOL_LINES_HL_COLOR_RED,
+                                       TOOL_LINES_HL_COLOR_GREEN,
+                                       TOOL_LINES_HL_COLOR_BLUE);
 
             {
               PolylineSceneLayer::Chain chain;
+              chain.push_back(start_);
+              chain.push_back(end_);
+              if(lineHighlightArea_ == LineHighlightArea_Segment)
+                polylineLayer->AddChain(chain, false, highlightColor);
+              else
+                polylineLayer->AddChain(chain, false, color);
+            }
+
+            // handles
+            {
+              {
+                PolylineSceneLayer::Chain chain;
               
-              //TODO: take DPI into account
-              AddSquare(chain, GetController()->GetScene(), end_, 
-                GetController()->GetHandleSideLengthS());
+                //TODO: take DPI into account
+                AddSquare(chain, lock->GetScene(), start_, 
+                          GetController()->GetHandleSideLengthS());
               
-              if (lineHighlightArea_ == LineHighlightArea_End)
-                polylineLayer->AddChain(chain, true, highlightColor);
-              else
-                polylineLayer->AddChain(chain, true, color);
+                if (lineHighlightArea_ == LineHighlightArea_Start)
+                  polylineLayer->AddChain(chain, true, highlightColor);
+                else
+                  polylineLayer->AddChain(chain, true, color);
+              }
+
+              {
+                PolylineSceneLayer::Chain chain;
+              
+                //TODO: take DPI into account
+                AddSquare(chain, lock->GetScene(), end_, 
+                          GetController()->GetHandleSideLengthS());
+              
+                if (lineHighlightArea_ == LineHighlightArea_End)
+                  polylineLayer->AddChain(chain, true, highlightColor);
+                else
+                  polylineLayer->AddChain(chain, true, color);
+              }
             }
           }
-
         }
         {
           // Set the text layer propreties
@@ -248,10 +256,10 @@
 
 #if ORTHANC_STONE_ENABLE_OUTLINED_TEXT == 1
           SetTextLayerOutlineProperties(
-            GetController()->GetScene(), layerHolder_, buf, ScenePoint2D(midX, midY), 0);
+            lock->GetScene(), layerHolder_, buf, ScenePoint2D(midX, midY), 0);
 #else
           SetTextLayerProperties(
-            GetController()->GetScene(), layerHolder_, buf, ScenePoint2D(midX, midY), 0);
+            lock->GetScene(), layerHolder_, buf, ScenePoint2D(midX, midY), 0);
 #endif
         }
       }
--- a/Framework/Scene2DViewport/MeasureToolsToolbox.cpp	Fri Nov 29 11:03:41 2019 +0100
+++ b/Framework/Scene2DViewport/MeasureToolsToolbox.cpp	Fri Nov 29 21:22:21 2019 +0100
@@ -306,28 +306,30 @@
     for (int i = startingLayerIndex; i < startingLayerIndex + 5; ++i)
     {
       TextSceneLayer* textLayer = layerHolder->GetTextLayer(i);
-      ORTHANC_ASSERT(textLayer != NULL);
-      textLayer->SetText(text);
-
-      if (i == startingLayerIndex + 4)
-      {
-        textLayer->SetColor(TEXT_COLOR_RED,
-          TEXT_COLOR_GREEN,
-          TEXT_COLOR_BLUE);
-      }
-      else
+      if (textLayer != NULL)
       {
-        textLayer->SetColor(TEXT_OUTLINE_COLOR_RED,
-          TEXT_OUTLINE_COLOR_GREEN,
-          TEXT_OUTLINE_COLOR_BLUE);
-      }
+        textLayer->SetText(text);
 
-      ScenePoint2D textAnchor;
-      int offIndex = i - startingLayerIndex;
-      ORTHANC_ASSERT(offIndex >= 0 && offIndex < 5);
-      textLayer->SetPosition(
-        p.GetX() + xoffsets[offIndex] * pixelToScene,
-        p.GetY() + yoffsets[offIndex] * pixelToScene);
+        if (i == startingLayerIndex + 4)
+        {
+          textLayer->SetColor(TEXT_COLOR_RED,
+                              TEXT_COLOR_GREEN,
+                              TEXT_COLOR_BLUE);
+        }
+        else
+        {
+          textLayer->SetColor(TEXT_OUTLINE_COLOR_RED,
+                              TEXT_OUTLINE_COLOR_GREEN,
+                              TEXT_OUTLINE_COLOR_BLUE);
+        }
+
+        ScenePoint2D textAnchor;
+        int offIndex = i - startingLayerIndex;
+        ORTHANC_ASSERT(offIndex >= 0 && offIndex < 5);
+        textLayer->SetPosition(
+          p.GetX() + xoffsets[offIndex] * pixelToScene,
+          p.GetY() + yoffsets[offIndex] * pixelToScene);
+      }
     }
   }
 #else
@@ -339,20 +341,21 @@
     , int layerIndex)
   {
     TextSceneLayer* textLayer = layerHolder->GetTextLayer(layerIndex);
-    ORTHANC_ASSERT(textLayer != NULL);
-    textLayer->SetText(text);
+    if (textLayer != NULL)
+    {
+      textLayer->SetText(text);
+      textLayer->SetColor(TEXT_COLOR_RED, TEXT_COLOR_GREEN, TEXT_COLOR_BLUE);
 
-    textLayer->SetColor(TEXT_COLOR_RED, TEXT_COLOR_GREEN, TEXT_COLOR_BLUE);
-
-    ScenePoint2D textAnchor;
-    textLayer->SetPosition(p.GetX(), p.GetY());
+      ScenePoint2D textAnchor;
+      textLayer->SetPosition(p.GetX(), p.GetY());
+    }
   }
 #endif 
 
-  std::ostream& operator<<(std::ostream& os, const ScenePoint2D& p)
-  {
-    os << "x = " << p.GetX() << " , y = " << p.GetY();
-    return os;
-  }
+  std::ostream& operator<<(std::ostream& os, const ScenePoint2D& p)
+  {
+    os << "x = " << p.GetX() << " , y = " << p.GetY();
+    return os;
+  }
 
 }
--- a/Framework/Scene2DViewport/MeasureTrackers.cpp	Fri Nov 29 11:03:41 2019 +0100
+++ b/Framework/Scene2DViewport/MeasureTrackers.cpp	Fri Nov 29 21:22:21 2019 +0100
@@ -53,23 +53,18 @@
       command_->Undo();
   }
 
-  Scene2D& CreateMeasureTracker::GetScene()
-  {
-    return controllerW_.lock()->GetScene();
-  }
-
   EditMeasureTracker::EditMeasureTracker(boost::weak_ptr<ViewportController> controllerW, const PointerEvent& e)
     : controllerW_(controllerW)
     , alive_(true)
     , commitResult_(true)
   {
     boost::shared_ptr<ViewportController> controller = controllerW.lock();
-    originalClickPosition_ = e.GetMainPosition().Apply(controller->GetScene().GetCanvasToSceneTransform());
-  }
 
-  Scene2D& EditMeasureTracker::GetScene()
-  {
-    return controllerW_.lock()->GetScene();
+    if (controller)
+    {
+      std::auto_ptr<IViewport::ILock> lock(controller->GetViewport().Lock());
+      originalClickPosition_ = e.GetMainPosition().Apply(lock->GetScene().GetCanvasToSceneTransform());
+    }
   }
 
   void EditMeasureTracker::Cancel()
--- a/Framework/Scene2DViewport/MeasureTrackers.h	Fri Nov 29 11:03:41 2019 +0100
+++ b/Framework/Scene2DViewport/MeasureTrackers.h	Fri Nov 29 21:22:21 2019 +0100
@@ -48,7 +48,6 @@
     boost::shared_ptr<CreateMeasureCommand>         command_;
     boost::weak_ptr<ViewportController>          controllerW_;
     bool                            alive_;
-    Scene2D&                      GetScene();
 
   private:
     bool                            commitResult_;
@@ -68,7 +67,6 @@
     boost::shared_ptr<EditMeasureCommand> command_;
     boost::weak_ptr<ViewportController>   controllerW_;
     bool                                  alive_;
-    Scene2D&            GetScene();
     
     ScenePoint2D                          GetOriginalClickPosition() const
     {
--- a/Framework/Scene2DViewport/ViewportController.cpp	Fri Nov 29 11:03:41 2019 +0100
+++ b/Framework/Scene2DViewport/ViewportController.cpp	Fri Nov 29 21:22:21 2019 +0100
@@ -136,20 +136,26 @@
     }
   }
 
-  const OrthancStone::AffineTransform2D& ViewportController::GetCanvasToSceneTransform() const
+  OrthancStone::AffineTransform2D ViewportController::GetCanvasToSceneTransform() const
   {
-    return GetScene().GetCanvasToSceneTransform();
+    std::auto_ptr<IViewport::ILock> lock(viewport_.Lock());
+    return lock->GetScene().GetCanvasToSceneTransform();
   }
 
-  const OrthancStone::AffineTransform2D& ViewportController::GetSceneToCanvasTransform() const
+  OrthancStone::AffineTransform2D ViewportController::GetSceneToCanvasTransform() const
   {
-    return GetScene().GetSceneToCanvasTransform();
+    std::auto_ptr<IViewport::ILock> lock(viewport_.Lock());
+    return lock->GetScene().GetSceneToCanvasTransform();
   }
 
   void ViewportController::SetSceneToCanvasTransform(
     const AffineTransform2D& transform)
   {
-    viewport_.GetScene().SetSceneToCanvasTransform(transform);
+    {
+      std::auto_ptr<IViewport::ILock> lock(viewport_.Lock());
+      lock->GetScene().SetSceneToCanvasTransform(transform);
+    }
+    
     BroadcastMessage(SceneTransformChanged(*this));
     
     // update the canvas to scene factor
@@ -160,16 +166,22 @@
   void ViewportController::FitContent(
     unsigned int canvasWidth, unsigned int canvasHeight)
   {
-    viewport_.GetScene().FitContent(canvasWidth, canvasHeight);
+    {
+      std::auto_ptr<IViewport::ILock> lock(viewport_.Lock());
+      lock->GetScene().FitContent(canvasWidth, canvasHeight);
+    }
+    
     BroadcastMessage(SceneTransformChanged(*this));
   }
 
   void ViewportController::FitContent()
   {
-    if (viewport_.HasCompositor())
+    std::auto_ptr<IViewport::ILock> lock(viewport_.Lock());
+
+    if (lock->HasCompositor())
     {
-      const ICompositor& compositor = viewport_.GetCompositor();
-      viewport_.GetScene().FitContent(compositor.GetCanvasWidth(), compositor.GetCanvasHeight());
+      const ICompositor& compositor = lock->GetCompositor();
+      lock->GetScene().FitContent(compositor.GetCanvasWidth(), compositor.GetCanvasHeight());
       BroadcastMessage(SceneTransformChanged(*this));
     }
   }
@@ -195,8 +207,8 @@
   {
     if (canvasToSceneFactor_ == 0)
     {
-      canvasToSceneFactor_ =
-        GetScene().GetCanvasToSceneTransform().ComputeZoom();
+      std::auto_ptr<IViewport::ILock> lock(viewport_.Lock());
+      canvasToSceneFactor_ = lock->GetScene().GetCanvasToSceneTransform().ComputeZoom();
     }
     return canvasToSceneFactor_;
   }
--- a/Framework/Scene2DViewport/ViewportController.h	Fri Nov 29 11:03:41 2019 +0100
+++ b/Framework/Scene2DViewport/ViewportController.h	Fri Nov 29 21:22:21 2019 +0100
@@ -112,10 +112,10 @@
     void SetActiveTracker(boost::shared_ptr<IFlexiblePointerTracker> tracker);
 
     /** Forwarded to the underlying scene */
-    const AffineTransform2D& GetCanvasToSceneTransform() const;
+    AffineTransform2D GetCanvasToSceneTransform() const;
 
     /** Forwarded to the underlying scene */
-    const AffineTransform2D& GetSceneToCanvasTransform() const;
+    AffineTransform2D GetSceneToCanvasTransform() const;
 
     /** Forwarded to the underlying scene, and broadcasted to the observers */
     void SetSceneToCanvasTransform(const AffineTransform2D& transform);
@@ -172,14 +172,9 @@
     /** forwarded to the UndoStack */
     bool CanRedo() const;
 
-    Scene2D& GetScene()
+    IViewport& GetViewport() const
     {
-      return viewport_.GetScene();
-    }
-
-    const Scene2D& GetScene() const
-    {
-      return const_cast<IViewport&>(viewport_).GetScene();
+      return viewport_;
     }
 
   private:
--- a/Framework/Viewport/IViewport.h	Fri Nov 29 11:03:41 2019 +0100
+++ b/Framework/Viewport/IViewport.h	Fri Nov 29 21:22:21 2019 +0100
@@ -29,25 +29,39 @@
   /**
    * Class that combines a Scene2D with a canvas where to draw the
    * scene. A call to "Refresh()" will update the content of the
-   * canvas.
+   * canvas. A "IViewport" can possibly be accessed from several
+   * threads depending on the rendering back-end (e.g. in SDL or Qt):
+   * The "ILock" subclass implements the locking mechanism to modify
+   * the content of the scene. 
+   *
+   * NB: The lock must be a "recursive_mutex", as the viewport
+   * controller can lock it a second time (TODO - Why so?).
    **/  
   class IViewport : public boost::noncopyable
   {
   public:
+    class ILock : public boost::noncopyable
+    {
+    public:
+      virtual ~ILock()
+      {
+      }
+
+      virtual Scene2D& GetScene() = 0;
+
+      virtual ScenePoint2D GetPixelCenterCoordinates(int x, int y) = 0;
+
+      virtual bool HasCompositor() const = 0;
+
+      virtual ICompositor& GetCompositor() = 0;
+    };   
+    
     virtual ~IViewport()
     {
     }
 
-    virtual Scene2D& GetScene() = 0;
-
     virtual void Refresh() = 0;
 
-    virtual ScenePoint2D GetPixelCenterCoordinates(int x, int y) const = 0;
-
-    virtual bool HasCompositor() const = 0;
-
-    virtual ICompositor& GetCompositor() = 0;
-
-    virtual const ICompositor& GetCompositor() const = 0;
+    virtual ILock* Lock() = 0;
   };
 }
--- a/Framework/Viewport/SdlViewport.cpp	Fri Nov 29 11:03:41 2019 +0100
+++ b/Framework/Viewport/SdlViewport.cpp	Fri Nov 29 21:22:21 2019 +0100
@@ -22,8 +22,6 @@
 
 #include <Core/OrthancException.h>
 
-#include <boost/make_shared.hpp>
-
 namespace OrthancStone
 {
   SdlOpenGLViewport::SdlOpenGLViewport(const char* title,
@@ -35,19 +33,9 @@
     compositor_.reset(new OpenGLCompositor(context_, GetScene()));
   }
 
-  SdlOpenGLViewport::SdlOpenGLViewport(const char* title,
-                                       unsigned int width,
-                                       unsigned int height,
-                                       boost::shared_ptr<Scene2D>& scene,
-                                       bool allowDpiScaling) :
-    SdlViewport(scene),
-    context_(title, width, height, allowDpiScaling)
-  {
-    compositor_.reset(new OpenGLCompositor(context_, GetScene()));
-  }
-
   void SdlOpenGLViewport::Refresh()
   {
+    boost::mutex::scoped_lock lock(mutex_);
     compositor_->Refresh();
   }
 
@@ -70,22 +58,29 @@
     }
   }
   
-  void SdlCairoViewport::Refresh()
+  void SdlCairoViewport::RefreshInternal()  // Assumes that the mutex is locked
   {
     compositor_.Refresh();
     window_.Render(sdlSurface_);
   }
 
+  void SdlCairoViewport::Refresh()
+  {
+    boost::mutex::scoped_lock lock(mutex_);
+    RefreshInternal();
+  }
+
   void SdlCairoViewport::UpdateSize(unsigned int width,
                                     unsigned int height)
   {
+    boost::mutex::scoped_lock lock(mutex_);
     compositor_.UpdateSize(width, height);
     UpdateSdlSurfaceSize(width, height);
-    Refresh();
+    RefreshInternal();
   }
   
   void SdlCairoViewport::UpdateSdlSurfaceSize(unsigned int width,
-                                              unsigned int height)
+                                              unsigned int height)  // Assumes that the mutex is locked
   {
     static const uint32_t rmask = 0x00ff0000;
     static const uint32_t gmask = 0x0000ff00;
--- a/Framework/Viewport/SdlViewport.h	Fri Nov 29 11:03:41 2019 +0100
+++ b/Framework/Viewport/SdlViewport.h	Fri Nov 29 21:22:21 2019 +0100
@@ -45,18 +45,14 @@
 {
   class SdlViewport : public ViewportBase
   {
+  protected:
+    boost::mutex  mutex_;
+
   public:
     SdlViewport()
     {
     }
 
-    SdlViewport(boost::shared_ptr<Scene2D>& scene) : 
-      ViewportBase(scene)
-    {
-    }
-
-    virtual SdlWindow& GetWindow() = 0;
-    
     virtual void UpdateSize(unsigned int width,
                             unsigned int height) = 0;
   };
@@ -66,52 +62,87 @@
   {
   private:
     SdlOpenGLContext  context_;
-    std::auto_ptr<OpenGLCompositor>   compositor_;
+    std::auto_ptr<OpenGLCompositor>  compositor_;
 
+    class SdlLock : public LockBase
+    {
+    private:
+      SdlOpenGLViewport&         that_;
+      boost::mutex::scoped_lock  lock_;
+      
+    public:
+      SdlLock(SdlOpenGLViewport& viewport) :
+        LockBase(viewport),
+        that_(viewport),
+        lock_(viewport.mutex_)
+      {
+      }
+
+      virtual bool HasCompositor() const ORTHANC_OVERRIDE
+      {
+        return true;
+      }
+
+      virtual ICompositor& GetCompositor() ORTHANC_OVERRIDE
+      {
+        return *that_.compositor_;
+      }
+    };
+    
   public:
     SdlOpenGLViewport(const char* title,
                       unsigned int width,
                       unsigned int height,
                       bool allowDpiScaling = true);
 
-    SdlOpenGLViewport(const char* title,
-                      unsigned int width,
-                      unsigned int height,
-                      boost::shared_ptr<Scene2D>& scene,
-                      bool allowDpiScaling = true);
+    virtual void Refresh() ORTHANC_OVERRIDE;
 
-    virtual SdlWindow& GetWindow() ORTHANC_OVERRIDE
+    virtual ILock* Lock() ORTHANC_OVERRIDE
     {
-      return context_.GetWindow();
+      return new SdlLock(*this);
     }
 
-    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 bool HasCompositor() const ORTHANC_OVERRIDE
-    {
-      return true;
-    }
-
-    virtual ICompositor& GetCompositor() ORTHANC_OVERRIDE
-    {
-      return *compositor_.get();
-    }
   };
 
 
   class SdlCairoViewport : public SdlViewport
   {
   private:
+    class SdlLock : public LockBase
+    {
+    private:
+      SdlCairoViewport&          that_;
+      boost::mutex::scoped_lock  lock_;
+      
+    public:
+      SdlLock(SdlCairoViewport& viewport) :
+        LockBase(viewport),
+        that_(viewport),
+        lock_(viewport.mutex_)
+      {
+      }
+
+      virtual bool HasCompositor() const ORTHANC_OVERRIDE
+      {
+        return true;
+      }
+
+      virtual ICompositor& GetCompositor() ORTHANC_OVERRIDE
+      {
+        return that_.compositor_;
+      }
+    };
+ 
     SdlWindow         window_;
     CairoCompositor   compositor_;
     SDL_Surface*      sdlSurface_;
 
-  private:
+    void RefreshInternal();
+    
     void UpdateSdlSurfaceSize(unsigned int width,
                               unsigned int height);
 
@@ -121,32 +152,11 @@
                      unsigned int height,
                      bool allowDpiScaling = true);
 
-    SdlCairoViewport(const char* title,
-                     unsigned int width,
-                     unsigned int height,
-                     boost::shared_ptr<Scene2D>& scene,
-                     bool allowDpiScaling = true);
-
     ~SdlCairoViewport();
 
-    virtual SdlWindow& GetWindow() ORTHANC_OVERRIDE
-    {
-      return window_;
-    }
-    
     virtual void Refresh() ORTHANC_OVERRIDE;
 
     virtual void UpdateSize(unsigned int width,
                             unsigned int height) ORTHANC_OVERRIDE;
-
-    virtual bool HasCompositor() const ORTHANC_OVERRIDE
-    {
-      return true;
-    }
-
-    virtual ICompositor& GetCompositor() ORTHANC_OVERRIDE
-    {
-      return compositor_;
-    }
   };
 }
--- a/Framework/Viewport/ViewportBase.cpp	Fri Nov 29 11:03:41 2019 +0100
+++ b/Framework/Viewport/ViewportBase.cpp	Fri Nov 29 21:22:21 2019 +0100
@@ -26,27 +26,11 @@
 
 namespace OrthancStone
 {
-  ViewportBase::ViewportBase() :
-    scene_(boost::make_shared<Scene2D>())
-  {
-  }
-
-  
-  ViewportBase::ViewportBase(boost::shared_ptr<Scene2D>& scene) :
-    scene_(scene)
-  {
-    if (scene.get() == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-    }
-  }
-  
-
-  ScenePoint2D ViewportBase::GetPixelCenterCoordinates(int x, int y) const
+  ScenePoint2D ViewportBase::LockBase::GetPixelCenterCoordinates(int x, int y)
   {
     if (HasCompositor())
     {
-      const ICompositor& compositor = GetCompositor();
+      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);
--- a/Framework/Viewport/ViewportBase.h	Fri Nov 29 11:03:41 2019 +0100
+++ b/Framework/Viewport/ViewportBase.h	Fri Nov 29 21:22:21 2019 +0100
@@ -29,25 +29,31 @@
   class ViewportBase : public IViewport
   {
   private:
-    boost::shared_ptr<Scene2D>  scene_;
+    Scene2D  scene_;
 
-  public:
-    ViewportBase();
-
-    ViewportBase(boost::shared_ptr<Scene2D>& scene);
-
-    virtual Scene2D& GetScene() ORTHANC_OVERRIDE
+  protected:
+    class LockBase : public ILock
     {
-      return *scene_;
-    }
+    private:
+      ViewportBase& that_;
 
-    virtual ScenePoint2D GetPixelCenterCoordinates(int x, int y) const ORTHANC_OVERRIDE;
+    public:
+      LockBase(ViewportBase& that) :
+        that_(that)
+      {
+      }
 
-    virtual const ICompositor& GetCompositor() const ORTHANC_OVERRIDE
+      virtual Scene2D& GetScene() ORTHANC_OVERRIDE
+      {
+        return that_.scene_;
+      }
+
+      virtual ScenePoint2D GetPixelCenterCoordinates(int x, int y) ORTHANC_OVERRIDE;
+    };
+
+    Scene2D& GetScene()
     {
-      IViewport* mutableThis = 
-        const_cast<IViewport*>(static_cast<const IViewport*>(this));
-      return mutableThis->GetCompositor();
+      return scene_;
     }
   };
 }