diff --git a/Config/FilterPlugin.ini b/Config/FilterPlugin.ini
deleted file mode 100644
index ccebca2f326481a66548a0f10cc21de1cf4bad74..0000000000000000000000000000000000000000
--- a/Config/FilterPlugin.ini
+++ /dev/null
@@ -1,8 +0,0 @@
-[FilterPlugin]
-; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
-; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
-;
-; Examples:
-;    /README.txt
-;    /Extras/...
-;    /Binaries/ThirdParty/*.dll
diff --git a/Content/InstanceMaterial.uasset b/Content/InstanceMaterial.uasset
new file mode 100644
index 0000000000000000000000000000000000000000..2fb267b53bfd278ae16752cd0e518422e251670e
--- /dev/null
+++ b/Content/InstanceMaterial.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fd8257ab5d1829b9ff175b290de9ffed71b9cb974d838f525de39089fdda5e02
+size 49757
diff --git a/Content/textmaterial.uasset b/Content/textmaterial.uasset
new file mode 100644
index 0000000000000000000000000000000000000000..ecb4b3901b8284e446d840a556f5dc86a565083c
--- /dev/null
+++ b/Content/textmaterial.uasset
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:28e0e621930bd6cfba91edf24730fcf6cde5528501d697e984309a2d4447753f
+size 24983
diff --git a/DasherVR.uplugin b/DasherVR.uplugin
index 5697244bb824b2d5ab7d1092a8b3ac431876bb83..b36b042eda04d4b521a7fd80db5a18e0e7f11ad7 100644
--- a/DasherVR.uplugin
+++ b/DasherVR.uplugin
@@ -20,5 +20,15 @@
 			"Type": "Runtime",
 			"LoadingPhase": "PreDefault"
 		}
+	],
+	"Plugins": [
+		{
+			"Name": "nDisplay",
+			"Enabled": true
+		},
+		{
+			"Name": "RWTHVRToolkit",
+			"Enabled": true
+		}
 	]
 }
diff --git a/Source/DasherVR/DasherVR.Build.cs b/Source/DasherVR/DasherVR.Build.cs
index d7f74ec1345122dd45a9b6295a765488486a4eb4..bf038bb08991a927c5ae61bfe0a2ecadc83a9817 100644
--- a/Source/DasherVR/DasherVR.Build.cs
+++ b/Source/DasherVR/DasherVR.Build.cs
@@ -6,7 +6,9 @@ public class DasherVR : ModuleRules
 {
 	public DasherVR(ReadOnlyTargetRules Target) : base(Target)
 	{
-		PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
+		PCHUsage = PCHUsageMode.NoSharedPCHs;
+		CppStandard = CppStandardVersion.Cpp17;
+		PrivatePCHHeaderFile = "Private/DasherVRPrivatePCH.h";
 		PrivateDefinitions.Add("HAVE_OWN_FILEUTILS");
 
 		PublicIncludePaths.AddRange(
@@ -40,7 +42,9 @@ public class DasherVR : ModuleRules
 				"Slate",
 				"SlateCore",
 				"UMG",
-				"RenderCore"
+				"RenderCore",
+				"DisplayCluster",
+				"RWTHVRToolkit"
 				// ... add private dependencies that you statically link with here ...	
 			}
 			);
diff --git a/Source/DasherVR/Private/DasherFileUtils.cpp b/Source/DasherVR/Private/DasherFileUtils.cpp
index 8f346a33bc02e3dee5e606b181ea025213995991..8e1f40a87d2f571c44af9affe5bd94e1cea891da 100644
--- a/Source/DasherVR/Private/DasherFileUtils.cpp
+++ b/Source/DasherVR/Private/DasherFileUtils.cpp
@@ -2,6 +2,8 @@
 #include "DasherCoreWrapper.h"
 #include "HAL/FileManagerGeneric.h"
 #include "Misc/FileHelper.h"
+#include "Misc/Paths.h"
+#include "Internationalization/Regex.h"
 
 namespace Dasher
 {
diff --git a/Source/DasherVR/Private/DasherInterface.cpp b/Source/DasherVR/Private/DasherInterface.cpp
index 9eaea7b3d89e0c61bf010850ada712ac7c84353a..b6b085be38e353ac3334ae91bd50eaa379499206 100644
--- a/Source/DasherVR/Private/DasherInterface.cpp
+++ b/Source/DasherVR/Private/DasherInterface.cpp
@@ -28,11 +28,11 @@ namespace Dasher
 	//The next functions operate on the buffer
 
 	//For now can only return one character around the cursor
-	std::string DasherInterface::GetTextAroundCursor(Dasher::CControlManager::EditDistance iDist)
+	std::string DasherInterface::GetTextAroundCursor(Dasher::EditDistance iDist)
 	{		
 		if (Buffer.Len()>Cursor)
 		{
-			if (iDist == Dasher::CControlManager::EditDistance::EDIT_CHAR)
+			if (iDist == Dasher::EditDistance::EDIT_CHAR)
 			{
 				const FString Output = Buffer.Mid(Cursor, 1);
 				return TCHAR_TO_UTF8(*Output);
@@ -47,9 +47,9 @@ namespace Dasher
 		return std::string("Cursor out of bounds");
 	}
 
-	unsigned int DasherInterface::ctrlMove(bool bForwards, Dasher::CControlManager::EditDistance dist)
+	unsigned int DasherInterface::ctrlMove(bool bForwards, Dasher::EditDistance dist)
 	{
-		if (dist == Dasher::CControlManager::EditDistance::EDIT_CHAR)
+		if (dist == Dasher::EditDistance::EDIT_CHAR)
 		{
 			if (bForwards) Cursor++;
 			else Cursor--;
@@ -57,9 +57,9 @@ namespace Dasher
 		return Cursor;
 	}
 
-	unsigned int DasherInterface::ctrlDelete(bool bForwards, Dasher::CControlManager::EditDistance dist)
+	unsigned int DasherInterface::ctrlDelete(bool bForwards, Dasher::EditDistance dist)
 	{
-		if (dist == Dasher::CControlManager::EditDistance::EDIT_CHAR)
+		if (dist == Dasher::EditDistance::EDIT_CHAR)
 		{
 			const int Index = Cursor - (bForwards ? 0 : 1);
 			const FString DeletedChar = Buffer.Mid(Index, 1);
@@ -129,7 +129,7 @@ namespace Dasher
 		NewFrame(time, true);
 	}
 
-	void DasherInterface::SetScreen(SDasherWidget* screen)
+	void DasherInterface::SetScreen(CDasherScreen* screen)
 	{
 		//set the widget as screen that dasher will be displayed on
 		ChangeScreen(screen);
diff --git a/Source/DasherVR/Private/DasherVRPrivatePCH.h b/Source/DasherVR/Private/DasherVRPrivatePCH.h
new file mode 100644
index 0000000000000000000000000000000000000000..e9cda3f89f2a68c2afdae9ce7dc82a88b39841fb
--- /dev/null
+++ b/Source/DasherVR/Private/DasherVRPrivatePCH.h
@@ -0,0 +1,3 @@
+#pragma once
+#include "CoreMinimal.h"
+#include "DasherVR.h"
\ No newline at end of file
diff --git a/Source/DasherVR/Private/SDasherWidget.cpp b/Source/DasherVR/Private/SDasherWidget.cpp
index 3e770e343de12e02f56c4f435d14aed58303401e..1d48f4b1c5fcea8f6a9809c9d6733a664389aea2 100644
--- a/Source/DasherVR/Private/SDasherWidget.cpp
+++ b/Source/DasherVR/Private/SDasherWidget.cpp
@@ -6,6 +6,7 @@
 #include "DasherInterface.h"
 
 #include "Components/SlateWrapperTypes.h"
+#include "Framework/Application/SlateApplication.h"
 
 BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
 
@@ -19,62 +20,173 @@ void SDasherWidget::SetEditor(bool EditorState)
 	IsEditor = EditorState;
 }
 
+void SDasherWidget::EnableInput(bool enable)
+{
+	InputPaused = !enable;
+}
+
+bool SDasherWidget::GetInputEnabled() const
+{
+	return !InputPaused;
+}
+
 //Event Handlers
 //Mouse position saved for mouse Input
-FReply SDasherWidget::HandleMouseMoveEvent(const FGeometry& Geometry, const FPointerEvent& MouseEvent)
+FReply SDasherWidget::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
+{
+    if (InputPaused) return FReply::Unhandled();
+
+	FMouseState& MouseState = GetMouseState(MouseEvent.GetPointerIndex());
+
+	if(MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton && !MouseState.PrimaryButtonPressed)
+	{
+		DasherMainInterface->KeyDown(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Primary_Input);
+		MouseState.PrimaryButtonPressed = true;
+		MouseListeners.ExecuteIfBound(MouseEvent.GetPointerIndex(), MouseState.PrimaryButtonPressed);
+	}
+	else if(MouseEvent.GetEffectingButton() == EKeys::RightMouseButton && !MouseState.SecondaryButtonPressed)
+	{
+		DasherMainInterface->KeyDown(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Secondary_Input);
+		MouseState.SecondaryButtonPressed = true;
+		BoostListeners.ExecuteIfBound(MouseEvent.GetPointerIndex(), MouseState.SecondaryButtonPressed);
+	}
+
+	UpdateMouseInteraction(MouseEvent.GetPointerIndex());
+	return FReply::Handled();
+}
+
+FMouseState& SDasherWidget::GetMouseState(int Index)
+{
+    if(!MouseStates.Contains(Index)) MouseStates.Add(Index, {});
+    return MouseStates[Index];
+}
+
+// Moves Item to Back
+void SDasherWidget::UpdateMouseInteraction(int Index)
+{
+    MouseInteractions.RemoveSingle(Index);
+	MouseInteractions.Add(Index);
+}
+
+int SDasherWidget::GetLastActiveMouseInteraction() const
+{
+	// Find Mouse that already interacted
+    for(int i = MouseInteractions.Num() - 1; i >= 0; i--)
+    {
+        if(MouseStates[MouseInteractions[i]].CurrentlyHovering) return MouseInteractions[i];
+    }
+
+	//None found, thus take the first one that currently hovers
+	for(const TPair<int, FMouseState>& Pair : MouseStates)
+	{
+	    if(Pair.Value.CurrentlyHovering) return Pair.Key;
+	}
+
+	return -1;
+}
+
+FReply SDasherWidget::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
 {
-	if (CurrentlyUsingVectorInput) return FReply::Unhandled();
+    if (InputPaused) return FReply::Unhandled();
+
+	FMouseState& MouseState = GetMouseState(MouseEvent.GetPointerIndex());
+
 	//The mouse event only contains the Screen Space Position
-	CursorPosition = Geometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition());
+	MouseState.CursorPosition = MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition());
+	MouseState.CurrentlyHovering = true; //ensure hover set
+
 	return FReply::Handled();
 }
 
-FReply SDasherWidget::HandleMouseDownEvent(const FGeometry& Geometry, const FPointerEvent& MouseEvent)
+void SDasherWidget::OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
 {
-	if (CurrentlyUsingVectorInput)
+	SWidget::OnMouseEnter(MyGeometry, MouseEvent);
+
+	FMouseState& MouseState = GetMouseState(MouseEvent.GetPointerIndex());
+
+	//Reenable if button is still pressed
+	if(MouseState.DisabledDueToMouseLeave)
 	{
-		CurrentlyUsingVectorInput = false;
-		return FReply::Handled().LockMouseToWidget(AsShared());
+	    if(MouseState.PrimaryButtonPressed && MouseEvent.IsMouseButtonDown(EKeys::LeftMouseButton))
+	    {
+	        DasherMainInterface->KeyDown(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Primary_Input);
+		    UpdateMouseInteraction(MouseEvent.GetPointerIndex());
+		    MouseListeners.ExecuteIfBound(MouseEvent.GetPointerIndex(), true);
+	    }
+	    if(MouseState.SecondaryButtonPressed && MouseEvent.IsMouseButtonDown(EKeys::RightMouseButton))
+	    {
+	        DasherMainInterface->KeyDown(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Secondary_Input);
+		    UpdateMouseInteraction(MouseEvent.GetPointerIndex());
+		    BoostListeners.ExecuteIfBound(MouseEvent.GetPointerIndex(), true);
+	    }
+	    MouseState.DisabledDueToMouseLeave = false;
 	}
-	DasherMainInterface->KeyDown(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), 100); //100 is the keycode for LMB
-	MouseDownListeners.ExecuteIfBound();
-	return FReply::Handled().LockMouseToWidget(AsShared());
 }
 
-FReply SDasherWidget::HandleMouseUpEvent(const FGeometry& Geometry, const FPointerEvent& MouseEvent)
+void SDasherWidget::OnMouseLeave(const FPointerEvent& MouseEvent)
 {
-	if (CurrentlyUsingVectorInput)
+	SWidget::OnMouseLeave(MouseEvent);
+
+	FMouseState& MouseState = GetMouseState(MouseEvent.GetPointerIndex());
+	MouseState.CurrentlyHovering = false;
+
+	if (InputPaused) return;
+
+	if(MouseState.PrimaryButtonPressed)
 	{
-		CurrentlyUsingVectorInput = false;
-		return FReply::Handled().ReleaseMouseLock();
+	    DasherMainInterface->KeyUp(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Primary_Input);
+		MouseListeners.ExecuteIfBound(MouseEvent.GetPointerIndex(), false);
+		MouseState.DisabledDueToMouseLeave = true;
 	}
-	DasherMainInterface->KeyUp(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), 100); //100 is the keycode for LMB
-    MouseUpListeners.ExecuteIfBound();
-	return FReply::Handled().ReleaseMouseLock();
+
+	if(MouseState.SecondaryButtonPressed)
+	{
+	    DasherMainInterface->KeyUp(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Secondary_Input);
+		BoostListeners.ExecuteIfBound(MouseEvent.GetPointerIndex(), false);
+		MouseState.DisabledDueToMouseLeave = true;
+	}
+}
+
+FReply SDasherWidget::OnMouseButtonDoubleClick(const FGeometry& InMyGeometry, const FPointerEvent& InMouseEvent)
+{
+    return OnMouseButtonUp(InMyGeometry, InMouseEvent);
 }
 
-FReply SDasherWidget::HandleMouseDoubleClickEvent(const FGeometry& Geometry, const FPointerEvent& MouseEvent)
+FReply SDasherWidget::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
 {
-	if(CurrentlyUsingVectorInput)
+	FMouseState& MouseState = GetMouseState(MouseEvent.GetPointerIndex());
+
+	if(MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton && MouseState.PrimaryButtonPressed)
+	{
+		DasherMainInterface->KeyUp(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Primary_Input);
+		MouseState.PrimaryButtonPressed = false;
+		MouseListeners.ExecuteIfBound(MouseEvent.GetPointerIndex(), MouseState.PrimaryButtonPressed);
+	}
+	else if(MouseEvent.GetEffectingButton() == EKeys::RightMouseButton && MouseState.SecondaryButtonPressed)
 	{
-		CurrentlyUsingVectorInput = false;
-		return FReply::Handled();
+		DasherMainInterface->KeyUp(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Secondary_Input);
+		MouseState.SecondaryButtonPressed = false;
+		BoostListeners.ExecuteIfBound(MouseEvent.GetPointerIndex(), MouseState.SecondaryButtonPressed);
 	}
-	DasherMainInterface->KeyUp(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), 100); //100 is the keycode for LMB
+
 	return FReply::Handled();
 }
 
 //Set in the HandleMouseMoveEvent or via Set InputVector
 //See tick for geometry things that could be use to modify this.
-FVector2D SDasherWidget::GetCursorPosition()
+FVector2D SDasherWidget::GetCursorPosition() const
 {
-	return CursorPosition;
+	const int lastInteraction = GetLastActiveMouseInteraction();
+
+	if(!MouseStates.Contains(lastInteraction)) return DasherMainInterface->ConvertDasher2Screen(Dasher::CDasherModel::ORIGIN_X, Dasher::CDasherModel::ORIGIN_Y); 
+
+	return MouseStates[lastInteraction].CursorPosition;
 }
 
 bool SDasherWidget::GetScreenCoords(screenint& iX, screenint& iY, Dasher::CDasherView* pView)
 {
 	const FVector2D Position = GetCursorPosition();
-	
+
 	iX = Position.X;
 	iY = Position.Y;
 
@@ -83,26 +195,58 @@ bool SDasherWidget::GetScreenCoords(screenint& iX, screenint& iY, Dasher::CDashe
 
 void SDasherWidget::InputVector(const FVector2D InputVector)
 {
-	if (!CurrentlyUsingVectorInput) return;
-	CursorPosition = DasherMainInterface->ConvertDasher2Screen(Dasher::CDasherModel::ORIGIN_X, Dasher::CDasherModel::ORIGIN_Y)
-					+ InputVector * FVector2D(1.0f,-1.0f) * (GetHeight() / 2);
+	if (InputPaused) return;
+
+	FMouseState& MouseState = GetMouseState(VectorInputPointerIndex);
+
+	MouseState.CursorPosition = DasherMainInterface->ConvertDasher2Screen(Dasher::CDasherModel::ORIGIN_X, Dasher::CDasherModel::ORIGIN_Y)
+		+ InputVector * FVector2D(1.0f, -1.0f) * (GetHeight() / 2);
+
+	MouseState.CurrentlyHovering = InputVector.Size() > 0.01; //Deadzone
 }
 
-void SDasherWidget::InputButton(bool Pressed)
+void SDasherWidget::VectorInputButton(bool Pressed)
 {
-	CurrentlyUsingVectorInput = true;
-	if(Pressed)
+	if (InputPaused) return;
+
+	FMouseState& MouseState = GetMouseState(VectorInputPointerIndex);
+
+	if (Pressed)
 	{
-		DasherMainInterface->KeyDown(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), 100); //100 is the keycode for LMB	
+		DasherMainInterface->KeyDown(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Primary_Input);
+		MouseState.PrimaryButtonPressed = true;
+	    UpdateMouseInteraction(VectorInputPointerIndex);
 	}
-	else {
-		DasherMainInterface->KeyUp(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), 100); //100 is the keycode for LMB
+	else
+	{
+		DasherMainInterface->KeyUp(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Primary_Input);
+		MouseState.PrimaryButtonPressed = false;
 	}
 }
 
+void SDasherWidget::VectorBoostButton(bool Pressed)
+{
+	if (InputPaused) return;
+
+	FMouseState& MouseState = GetMouseState(VectorInputPointerIndex);
+
+	if (Pressed)
+	{
+		DasherMainInterface->KeyDown(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Secondary_Input);
+		MouseState.SecondaryButtonPressed = true;
+	}
+	else
+	{
+		DasherMainInterface->KeyUp(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Secondary_Input);
+		MouseState.SecondaryButtonPressed = false;
+	}
+
+	UpdateMouseInteraction(VectorInputPointerIndex);
+}
+
 //The construction of the Dasher Widget, Dasher parameters are set here, a function to set them outside is not provided, but can be easily implemented
 void SDasherWidget::Construct(const FArguments& InArgs)
-{	
+{
 	//Initial resize, needed for setup
 	Width = InArgs._width;
 	Height = InArgs._height;
@@ -113,65 +257,57 @@ void SDasherWidget::Construct(const FArguments& InArgs)
 
 	//Setting up Dasher
 	static Dasher::XMLErrorDisplay display;
-    Dasher::XmlSettingsStore* Settings = new Dasher::XmlSettingsStore("Settings.xml", &display); //Gets deleted somewhere else
-    Settings->Load();
+	Dasher::XmlSettingsStore* Settings = new Dasher::XmlSettingsStore("Settings.xml", &display); //Gets deleted somewhere else
+	Settings->Load();
 	Settings->Save();
-    DasherMainInterface = MakeShared<Dasher::DasherInterface>(Settings);
-	DasherMainInterface->SetDefaultInputDevice(this);
+	DasherMainInterface = MakeShared<Dasher::DasherInterface>(Settings);
+	DasherMainInterface->GetModuleManager()->RegisterInputDeviceModule(this, true);
 
 	DasherMainInterface->SetScreen(this);
 	DasherMainInterface->SetBuffer(0);
 
-	DasherMainInterface->SetCharEnteredCallback([this](FString Char, FString Buffer){CharacterEntered.ExecuteIfBound(Char, Buffer);});
-	DasherMainInterface->SetCharDeletedCallback([this](FString Char, FString Buffer){CharacterDeleted.ExecuteIfBound(Char, Buffer);});
-
-	//Set up the Event Handlers for Mouse Movement etc.
-	SetOnMouseMove(FPointerEventHandler::CreateLambda([this](const FGeometry& Geometry, const FPointerEvent& MouseEvent) {return HandleMouseMoveEvent(Geometry, MouseEvent) ; }));
-	SetOnMouseButtonDown(FPointerEventHandler::CreateLambda([this](const FGeometry& Geometry, const FPointerEvent& MouseEvent) {return HandleMouseDownEvent(Geometry, MouseEvent); }));
-	SetOnMouseButtonUp(FPointerEventHandler::CreateLambda([this](const FGeometry& Geometry, const FPointerEvent& MouseEvent) {return HandleMouseUpEvent(Geometry, MouseEvent); }));
-	SetOnMouseDoubleClick(FPointerEventHandler::CreateLambda([this](const FGeometry& Geometry, const FPointerEvent& MouseEvent) {return HandleMouseDoubleClickEvent(Geometry, MouseEvent); })); //We treat a double click of the mouse as a lift of the mouse button.
-    //You could add Slate Code here for child slots etc.
+	DasherMainInterface->SetCharEnteredCallback([this](FString Char, FString Buffer)
+	{
+		CharacterEntered.ExecuteIfBound(Char, Buffer);
+		CharEnteredInFrame = true;
+		BufferAltered.ExecuteIfBound(Buffer);
+	});
+	DasherMainInterface->SetCharDeletedCallback([this](FString Char, FString Buffer)
+	{
+		CharacterDeleted.ExecuteIfBound(Char, Buffer);
+		CharDeletedInFrame = true;
+		BufferAltered.ExecuteIfBound(Buffer);
+	});
 }
 
-void SDasherWidget::SetBoolParamter(FString ParameterName, bool Value)
+void SDasherWidget::SetParameter(FString& ParameterName, bool Value)
 {
-	for(Dasher::Settings::bp_table Setting : Dasher::Settings::boolparamtable)
-	{
-		if(FString(Setting.regName).Compare(ParameterName, ESearchCase::IgnoreCase))
+	for (auto param = Dasher::Settings::parameter_defaults.begin(); param != Dasher::Settings::parameter_defaults.end(); ++param)
+		if (param->second.name == TCHAR_TO_UTF8(*ParameterName))
 		{
-			DasherMainInterface->SetBoolParameter(Setting.key, Value);
+			DasherMainInterface->SetBoolParameter(param->first, Value);
 			return;
 		}
-	}
 }
 
-void SDasherWidget::SetLongParamter(FString ParameterName, int64 Value)
+void SDasherWidget::SetParameter(FString& ParameterName, int64 Value)
 {
-	for(Dasher::Settings::bp_table Setting : Dasher::Settings::boolparamtable)
-	{
-		if(FString(Setting.regName).Compare(ParameterName, ESearchCase::IgnoreCase))
+	for (auto param = Dasher::Settings::parameter_defaults.begin(); param != Dasher::Settings::parameter_defaults.end(); ++param)
+		if (param->second.name == TCHAR_TO_UTF8(*ParameterName))
 		{
-			DasherMainInterface->SetLongParameter(Setting.key, Value);
+			DasherMainInterface->SetLongParameter(param->first, Value);
 			return;
 		}
-	}
 }
 
-void SDasherWidget::SetStringParamter(FString ParameterName, FString Value)
+void SDasherWidget::SetParameter(FString& ParameterName, FString Value)
 {
-	for(Dasher::Settings::bp_table Setting : Dasher::Settings::boolparamtable)
-	{
-		if(FString(Setting.regName).Compare(ParameterName, ESearchCase::IgnoreCase))
+	for (auto param = Dasher::Settings::parameter_defaults.begin(); param != Dasher::Settings::parameter_defaults.end(); ++param)
+		if (param->second.name == TCHAR_TO_UTF8(*ParameterName))
 		{
-			DasherMainInterface->SetStringParameter(Setting.key, TCHAR_TO_UTF8(*Value));
+			DasherMainInterface->SetStringParameter(param->first, TCHAR_TO_UTF8(*Value));
 			return;
 		}
-	}
-}
-
-//Set the colour Scheme for dasher
-void SDasherWidget::SetColourScheme(const Dasher::CColourIO::ColourInfo* pcolours) {
-	ColorPalette = pcolours;
 }
 
 //paints our stuff, then lets compoundwidget (super::) draw its stuff
@@ -189,45 +325,49 @@ int32 SDasherWidget::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGe
 	FSlateFontInfo Font;
 
 	TArray<TUniquePtr<DasherDrawGeometry>>& GeometryBuffer = *FrontBuffer;
-	for(TUniquePtr<DasherDrawGeometry>& GeneralObject : GeometryBuffer) {
-		switch(GeneralObject->Type)
+	for (TUniquePtr<DasherDrawGeometry>& GeneralObject : GeometryBuffer)
+	{
+		switch (GeneralObject->Type)
 		{
-			case Rectangle:
-				RectObject = static_cast<FFilledRect*>(GeneralObject.Get());
-				Size = {FMath::Abs(RectObject->top.X - RectObject->bottom.X), FMath::Abs(RectObject->top.Y - RectObject->bottom.Y)};
-				FSlateDrawElement::MakeBox(OutDrawElements, LayerId++, AllottedGeometry.ToPaintGeometry(Size, FSlateLayoutTransform(1, RectObject->top)), &MyBrush, ESlateDrawEffect::None, RectObject->color); //actually adds the box to the geometry
-				break;
-			case Writing:
-				WritingObject = static_cast<FWriting*>(GeneralObject.Get());
-				Text = FString(UTF8_TO_TCHAR(WritingObject->label->m_strText.c_str()));
-
-		        Font = FCoreStyle::GetDefaultFontStyle("Roboto", WritingObject->size, FFontOutlineSettings::NoOutline); //get the font
-				FSlateDrawElement::MakeText(OutDrawElements, LayerId++, AllottedGeometry.ToPaintGeometry(FSlateLayoutTransform(1, FVector2D(WritingObject->pos.X, WritingObject->pos.Y))), Text , Font, ESlateDrawEffect::None, WritingObject->color);
-				break;
-			case PolyLine:
-				LineObject = static_cast<FPolyLine*>(GeneralObject.Get());
-				FSlateDrawElement::MakeLines(OutDrawElements, LayerId++, AllottedGeometry.ToPaintGeometry(), LineObject->points, ESlateDrawEffect::None, LineObject->color, true, LineObject->linewidth);
-				break;
-			default: break;
+		case GeometryType::Rectangle:
+			RectObject = static_cast<FFilledRect*>(GeneralObject.Get());
+			Size = {FMath::Abs(RectObject->top.X - RectObject->bottom.X), FMath::Abs(RectObject->top.Y - RectObject->bottom.Y)};
+			FSlateDrawElement::MakeBox(OutDrawElements, LayerId++, AllottedGeometry.ToPaintGeometry(Size, FSlateLayoutTransform(1, RectObject->top)), &MyBrush, ESlateDrawEffect::None, RectObject->color); //actually adds the box to the geometry
+			break;
+		case GeometryType::Writing:
+			WritingObject = static_cast<FWriting*>(GeneralObject.Get());
+			Text = FString(*WritingObject->label);
+
+			Font = FCoreStyle::GetDefaultFontStyle("Roboto", WritingObject->size, FFontOutlineSettings::NoOutline); //get the font
+			FSlateDrawElement::MakeText(OutDrawElements, LayerId++, AllottedGeometry.ToPaintGeometry(FSlateLayoutTransform(1, FVector2D(WritingObject->pos.X, WritingObject->pos.Y))), Text, Font, ESlateDrawEffect::None, WritingObject->color);
+			break;
+		case GeometryType::PolyLine:
+			LineObject = static_cast<FPolyLine*>(GeneralObject.Get());
+			FSlateDrawElement::MakeLines(OutDrawElements, LayerId++, AllottedGeometry.ToPaintGeometry(), LineObject->points, ESlateDrawEffect::None, LineObject->color, true, LineObject->linewidth);
+			break;
+		default: break;
 		}
 	}
 
-	return SCompoundWidget::OnPaint(Args, AllottedGeometry, MyClippingRect, OutDrawElements, LayerId, InWidgetStyle,  bParentEnabled); //call the parent onPaint
+	return SCompoundWidget::OnPaint(Args, AllottedGeometry, MyClippingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled); //call the parent onPaint
 }
 
 //The tick function, we tick dasher in it and update the screen size for dasher
-void SDasherWidget::Tick(const FGeometry& AllotedGeometry, const double InCurrentTime, const float InDeltaTime) {
+void SDasherWidget::Tick(const FGeometry& AllotedGeometry, const double InCurrentTime, const float InDeltaTime)
+{
 	//don't tick in the editor
-	if (!IsEditor) {
+	if (!IsEditor)
+	{
 		SCompoundWidget::Tick(AllotedGeometry, InCurrentTime, InDeltaTime);
 
 		DasherMainInterface->Tick(static_cast<unsigned long>(InCurrentTime * 1000.0)); //we need to provide ticks in milliseconds
 
 
 		//This probably doesn't have to be done every tick, but it does not seem to have a huge hit on performance
-        const FGeometry Geometry = GetTickSpaceGeometry();
-        const FIntPoint AbsoluteSize = Geometry.Size.IntPoint();
-		if (Width != AbsoluteSize.X || Height != AbsoluteSize.Y) {
+		const FGeometry Geometry = GetTickSpaceGeometry();
+		const FIntPoint AbsoluteSize = Geometry.Size.IntPoint();
+		if (Width != AbsoluteSize.X || Height != AbsoluteSize.Y)
+		{
 			Width = AbsoluteSize.X;
 			Height = AbsoluteSize.Y;
 			resize(Width, Height);
@@ -245,86 +385,84 @@ std::pair<SDasherWidget::screenint, SDasherWidget::screenint> SDasherWidget::Tex
 }
 
 //Double Buffers are rotated here.
-void SDasherWidget::Display() {
+void SDasherWidget::Display()
+{
 	std::swap(FrontBuffer, BackBuffer);
 	BackBuffer->Empty();
-}
 
-
-//Functions for Drawing
-void SDasherWidget::DrawRectangle(Dasher::screenint x1, Dasher::screenint y1, Dasher::screenint x2, Dasher::screenint y2, int colour, int iOutlineColour, int iThickness)
-{
-	const FVector2D Top(x1, y1);
-	const FVector2D Bottom(x2, y2);
-	
-	FLinearColor RectColor;
-
-	if (ColorPalette) {
-		RectColor = FLinearColor(ColorPalette->Colors[colour].Red/255.0, ColorPalette->Colors[colour].Green / 255.0, ColorPalette->Colors[colour].Blue / 255.0);
+	if(CharEnteredInFrame && CharDeletedInFrame)
+	{
+		CharacterSwitchedLastFrame.ExecuteIfBound();
 	}
-	else {
-		RectColor = FLinearColor::Blue;
+	else if(CharEnteredInFrame)
+	{
+		CharacterEnteredLastFrame.ExecuteIfBound();
+	}
+	else if(CharDeletedInFrame)
+	{
+		CharacterDeletedLastFrame.ExecuteIfBound();
 	}
-	
-	BackBuffer->Add(MakeUnique<FFilledRect>(Top, Bottom, RectColor));
-}
-
-void SDasherWidget::DrawString(CDasherScreen::Label* lab, screenint x1, screenint y1, unsigned int iSize, int colour) {
-	const FVector2D Pos(x1, y1);
 
-	FLinearColor TextColor;
+	CharEnteredInFrame = false;
+	CharDeletedInFrame = false;
 
-	if (ColorPalette) {
-		TextColor = FLinearColor(ColorPalette->Colors[colour].Red / 255.0, ColorPalette->Colors[colour].Green / 255.0, ColorPalette->Colors[colour].Blue / 255.0);
-	}
-	else {
-		TextColor = FLinearColor::Black;
-	}
-	
-	BackBuffer->Add(MakeUnique<FWriting>(lab, Pos, static_cast<int>(iSize), TextColor));
+	DasherFrameCompleted.ExecuteIfBound();
 }
 
-void SDasherWidget::Polyline(CDasherScreen::point* points, int number, int iwidth, int colour) {
-	
-	FLinearColor LinearColor;
+//Only for testing
+#define s(X) static_cast<uint8>(FMath::Clamp(X*(((colour>= 10 && colour <= 36) || (colour>= 140 && colour <= 166)) ? brightness : 1.0f),0.0f,255.0f))
 
-	if (ColorPalette) {
-		LinearColor = FLinearColor(ColorPalette->Colors[colour].Red / 255.0, ColorPalette->Colors[colour].Green / 255.0, ColorPalette->Colors[colour].Blue / 255.0);
-	}
-	else {
-		LinearColor = FLinearColor::Blue;
-	}
+//Functions for Drawing
+void SDasherWidget::DrawRectangle(Dasher::screenint x1, Dasher::screenint y1, Dasher::screenint x2, Dasher::screenint y2, const Dasher::ColorPalette::Color& color, const Dasher::ColorPalette::Color& outlineColor, int iThickness)
+{
+    if (outlineColor == Dasher::ColorPalette::noColor) iThickness = 0; // Draw till brim if no outline color is given
+
+    if (color != Dasher::ColorPalette::noColor && !color.isFullyTransparent())
+    {
+        BackBuffer->Add(MakeUnique<FFilledRect>(FVector2D(x1 + iThickness, y1 + iThickness), FVector2D(x2 - iThickness, y2 - iThickness), FLinearColor(color.Red / 255.0f, color.Green / 255.0f, color.Blue / 255.0f, color.Alpha / 255.0f)));
+    }
+
+    if (iThickness && outlineColor != Dasher::ColorPalette::noColor && !outlineColor.isFullyTransparent())
+    {
+        const float hThickness = iThickness / 2.0f;
+        const FVector2D CornerMin = FVector2D(x1 + hThickness, y1 + hThickness);
+        const FVector2D CornerMax = FVector2D(x2 - hThickness, y2 - hThickness);
+
+        const FLinearColor oColor = FLinearColor(outlineColor.Red / 255.0f, outlineColor.Green / 255.0f, outlineColor.Blue / 255.0f, outlineColor.Alpha / 255.0f);
+        BackBuffer->Add(MakeUnique<FPolyLine>(TArray({ FVector2D(CornerMin.X, CornerMin.Y - hThickness),FVector2D(CornerMin.X, CornerMax.Y + hThickness) }), static_cast<float>(iThickness), oColor, false));
+        BackBuffer->Add(MakeUnique<FPolyLine>(TArray({ FVector2D(CornerMin.X - hThickness, CornerMax.Y),FVector2D(CornerMax.X + hThickness, CornerMax.Y) }), static_cast<float>(iThickness), oColor, false));
+        BackBuffer->Add(MakeUnique<FPolyLine>(TArray({ FVector2D(CornerMax.X, CornerMax.Y + hThickness),FVector2D(CornerMax.X, CornerMin.Y - hThickness) }), static_cast<float>(iThickness), oColor, false));
+        BackBuffer->Add(MakeUnique<FPolyLine>(TArray({ FVector2D(CornerMax.X + hThickness, CornerMin.Y),FVector2D(CornerMin.X - hThickness, CornerMin.Y) }), static_cast<float>(iThickness), oColor, false));
+    }
+}
 
-	TArray<FVector2D> PointArray;
-	for (int i = 0; i < number; i++) {
-		FVector2D Point(points[i].x, points[i].y);
-		PointArray.Add(Point);
-	}
-	
-	BackBuffer->Add(MakeUnique<FPolyLine>(PointArray, static_cast<float>(iwidth), LinearColor));
+void SDasherWidget::DrawString(CDasherScreen::Label* lab, screenint x1, screenint y1, unsigned int iSize, const Dasher::ColorPalette::Color& color) {
+    BackBuffer->Add(MakeUnique<FWriting>(UTF8_TO_TCHAR(lab->m_strText.c_str()), FVector2D(x1, y1), static_cast<int>(iSize), FLinearColor(color.Red / 255.0f, color.Green / 255.0f, color.Blue / 255.0f, color.Alpha / 255.0f)));
 }
 
-//techincally polygons are just multiple polylines. Dasher doesn't actually draw polygons in our case.
-void SDasherWidget::Polygon(CDasherScreen::point* points, int number, int fillcolour, int outlinecolour, int iwidth) {
-	FLinearColor LinearColor;
+void SDasherWidget::Polyline(CDasherScreen::point* points, int number, int iwidth, const Dasher::ColorPalette::Color& color) {
+    TArray<FVector2D> PointArray;
+    for (int i = 0; i < number; i++) {
+        FVector2D Point(points[i].x, points[i].y);
+        PointArray.Add(Point);
+    }
 
-	if (ColorPalette) {
-		LinearColor = FLinearColor(ColorPalette->Colors[outlinecolour].Red / 255.0, ColorPalette->Colors[outlinecolour].Green / 255.0, ColorPalette->Colors[outlinecolour].Blue / 255.0);
-	}
-	else {
-		LinearColor = FLinearColor::Blue;
-	}
-
-	TArray<FVector2D> PointArray;
-	for (int i = 0; i < number; i++) {
-		FVector2D Point(points[i].x, points[i].y);
-		PointArray.Add(Point);
-	}
-	PointArray.Add(FVector2D(points[0].x, points[0].y));
+    BackBuffer->Add(MakeUnique<FPolyLine>(PointArray, static_cast<float>(iwidth), FLinearColor(color.Red / 255.0f, color.Green / 255.0f, color.Blue / 255.0f, color.Alpha / 255.0f), true));
+}
 
-	BackBuffer->Add(MakeUnique<FPolyLine>(PointArray, static_cast<float>(iwidth), LinearColor));
+//techincally polygons are just multiple polylines. Dasher doesn't actually draw polygons in our case.
+void SDasherWidget::Polygon(CDasherScreen::point* points, int number, const Dasher::ColorPalette::Color& fillcolor, const Dasher::ColorPalette::Color& outlinecolor, int iwidth) {
+    TArray<FVector2D> PointArray;
+    for (int i = 0; i < number; i++) {
+        FVector2D Point(points[i].x, points[i].y);
+        PointArray.Add(Point);
+    }
+    PointArray.Add(FVector2D(points[0].x, points[0].y));
+
+    BackBuffer->Add(MakeUnique<FPolyLine>(PointArray, static_cast<float>(iwidth), FLinearColor(outlinecolor.Red / 255.0f, outlinecolor.Green / 255.0f, outlinecolor.Blue / 255.0f, outlinecolor.Alpha / 255.0f), true));
 }
 
+
 //We pass through the contents of the dasher buffer
 FString SDasherWidget::GetBuffer() const
 {
@@ -341,6 +479,7 @@ void SDasherWidget::StartTraining(FString PathToTextFile)
 	DasherMainInterface->ImportTrainingFile(TCHAR_TO_UTF8(*FPaths::ConvertRelativePathToFull(PathToTextFile)));
 }
 
+
 // ++ We need to undefine this namespace after we finish creating the Slate widget
 #undef LOCTEXT_NAMESPACE
 
diff --git a/Source/DasherVR/Private/UDasherWidget.cpp b/Source/DasherVR/Private/UDasherWidget.cpp
index 29031715ffeb8bf132a0e7b038f7b8eeec9bfcec..ca9fef7ac5665402d28c5b7da55b73042a9a5e09 100644
--- a/Source/DasherVR/Private/UDasherWidget.cpp
+++ b/Source/DasherVR/Private/UDasherWidget.cpp
@@ -1,5 +1,5 @@
 /*
-	Adapted from JoySoftEdgeImage by Rama https://nerivec.github.io/old-ue4-wiki/pages/umg-custom-widget-components-and-render-code-usable-in-umg-designer.html
+  Adapted from JoySoftEdgeImage by Rama https://nerivec.github.io/old-ue4-wiki/pages/umg-custom-widget-components-and-render-code-usable-in-umg-designer.html
 */
 #include "UDasherWidget.h"
 
@@ -9,7 +9,7 @@
 /////////////////////////////////////////////////////
 // UDasherWidget
 UDasherWidget::UDasherWidget(const FObjectInitializer& ObjectInitializer)
-	: Super(ObjectInitializer)
+  : Super(ObjectInitializer)
 {
 	//Default Values Set Here, see above
 }
@@ -30,78 +30,89 @@ void UDasherWidget::SynchronizeProperties()
 
 	//Check if we're in Editor
 	DasherScreen->SetEditor(IsDesignTime());
-	DasherScreen->CharacterEntered.BindLambda([this](FString Char, FString Buffer)
-	{
-		CharacterEntered.Broadcast(Char, Buffer);
-		BufferAltered.Broadcast(Buffer);
-	});
-	DasherScreen->CharacterDeleted.BindLambda([this](FString Char, FString Buffer)
-	{
-		CharacterDeleted.Broadcast(Char, Buffer);
-		BufferAltered.Broadcast(Buffer);
-	});
-	DasherScreen->MouseUpListeners.BindLambda([this]() {MouseEvent.Broadcast(false); });
-	DasherScreen->MouseDownListeners.BindLambda([this]() {MouseEvent.Broadcast(true); });
+	DasherScreen->CharacterEntered.BindLambda([this](FString Char, FString Buffer){CharacterEntered.Broadcast(Char, Buffer); });
+	DasherScreen->CharacterDeleted.BindLambda([this](FString Char, FString Buffer){CharacterDeleted.Broadcast(Char, Buffer); });
+	DasherScreen->MouseListeners.BindLambda([this](int index, bool pressed) { MouseEvent.Broadcast(index, pressed); });
+	DasherScreen->BoostListeners.BindLambda([this](int index, bool pressed) { BoostEvent.Broadcast(index, pressed); });
+	DasherScreen->BufferAltered.BindLambda([this](FString Buffer) { BufferAltered.Broadcast(Buffer); });
+	DasherScreen->CharacterEnteredLastFrame.BindLambda([this](){	CharacterEnteredLastFrame.Broadcast();	});
+	DasherScreen->CharacterDeletedLastFrame.BindLambda([this](){	CharacterDeletedLastFrame.Broadcast();	});
+	DasherScreen->CharacterSwitchedLastFrame.BindLambda([this](){	CharacterSwitchedLastFrame.Broadcast();	});
+	DasherScreen->DasherFrameCompleted.BindLambda([this]() { DasherFrameCompleted.Broadcast(); });
 }
 
 void UDasherWidget::ReleaseSlateResources(bool bReleaseChildren)
 {
 	Super::ReleaseSlateResources(bReleaseChildren);
 
-	DasherScreen.Reset();
+	if(bReleaseChildren) DasherScreen.Reset();
 }
 
 FString UDasherWidget::GetBuffer()
 {
+	if(!DasherScreen) return "";
 	return DasherScreen->GetBuffer();
 }
 
 void UDasherWidget::ResetBuffer()
 {
+	if(!DasherScreen) return;
 	DasherScreen->ResetBuffer();
 	BufferAltered.Broadcast("");
 }
 
 void UDasherWidget::StartTraining(FString PathToTextFile)
 {
+	if(!DasherScreen) return;
 	DasherScreen->StartTraining(PathToTextFile);
 }
 
 void UDasherWidget::SetBoolParamter(FString ParameterName, bool Value)
 {
-	DasherScreen->SetBoolParamter(ParameterName, Value);
+	if(!DasherScreen) return;
+	DasherScreen->SetParameter(ParameterName, Value);
 }
 
 void UDasherWidget::SetLongParamter(FString ParameterName, int64 Value)
 {
-	DasherScreen->SetLongParamter(ParameterName, Value);
+	if(!DasherScreen) return;
+	DasherScreen->SetParameter(ParameterName, Value);
 }
 
 void UDasherWidget::SetStringParamter(FString ParameterName, FString Value)
 {
-	DasherScreen->SetStringParamter(ParameterName, Value);
+	if(!DasherScreen) return;
+	DasherScreen->SetParameter(ParameterName, Value);
 }
 
 void UDasherWidget::InputButton(bool Pressed)
 {
-	DasherScreen->InputButton(Pressed);
+	if(!DasherScreen) return;
+	DasherScreen->VectorInputButton(Pressed);
 }
 
 void UDasherWidget::InputVector(FVector2D InputVector)
 {
-	DasherScreen->InputVector(InputVector);
+	if(!DasherScreen) return;
+  DasherScreen->InputVector(InputVector);
 }
 
-#if WITH_EDITOR
+bool UDasherWidget::GetInputEnabled() const
+{
+	if(!DasherScreen) return false;
+	return DasherScreen->GetInputEnabled();
+}
 
-//const FSlateBrush* UDasherWidget::GetEditorIcon()
-//{
-//	return FUMGStyle::Get().GetBrush("Widget.Image");
-//}
+void UDasherWidget::EnableInput(bool enabled)
+{
+	if(!DasherScreen) return;
+	DasherScreen->EnableInput(enabled);
+}
 
+#if WITH_EDITOR
 const FText UDasherWidget::GetPaletteCategory()
 {
-	return LOCTEXT("Common", "Common");
+  return LOCTEXT("Common", "Common");
 }
 
 #endif
@@ -109,4 +120,4 @@ const FText UDasherWidget::GetPaletteCategory()
 
 /////////////////////////////////////////////////////
 
-#undef LOCTEXT_NAMESPACE
\ No newline at end of file
+#undef LOCTEXT_NAMESPACE
diff --git a/Source/DasherVR/Public/DasherInterface.h b/Source/DasherVR/Public/DasherInterface.h
index b7427f2e4032b97ff8fecff814108aab6a60592b..6ff93a03db10f9d7c146f9293c49dfa47f245295 100644
--- a/Source/DasherVR/Public/DasherInterface.h
+++ b/Source/DasherVR/Public/DasherInterface.h
@@ -37,7 +37,7 @@ namespace Dasher
 		/// For character around cursor decision is arbitrary. Let's settle for character before cursor.
 		/// TODO. Consistently name functions dealing with dasher context, versus functions dealing with editor text.
 		/// I.E. GetAllContext should be named GetAllTtext
-		virtual std::string GetTextAroundCursor(Dasher::CControlManager::EditDistance) override;
+		virtual std::string GetTextAroundCursor(Dasher::EditDistance) override;
 
 		
 		///Called to execute a control-mode "move" command.
@@ -45,7 +45,7 @@ namespace Dasher
 		///\param dist how far to move: character, word, line, file. (Usually defined
 		/// by OS, e.g. for non-european languages)
 		///\return the offset, into the edit buffer of the cursor *after* the move.
-		virtual unsigned int ctrlMove(bool bForwards, Dasher::CControlManager::EditDistance dist) override;
+		virtual unsigned int ctrlMove(bool bForwards, Dasher::EditDistance dist) override;
 
 		
 		///Called to execute a control-mode "delete" command.
@@ -54,7 +54,7 @@ namespace Dasher
 		/// by OS, e.g. for non-european languages)
 		///\return the offset, into the edit buffer, of the cursor *after* the delete
 		/// (for forwards deletion, this will be the same as the offset *before
-		virtual unsigned int ctrlDelete(bool bForwards, Dasher::CControlManager::EditDistance dist) override;
+		virtual unsigned int ctrlDelete(bool bForwards, Dasher::EditDistance dist) override;
 		
 		///Clears all written text from edit buffer and rebuilds the model. The default
 		/// implementation does this using the control mode editDelete mechanism
@@ -75,7 +75,7 @@ namespace Dasher
 		void Tick(unsigned long time);
 		
 		// set the screen, needed for maximum spaghetti, to make changeScreen accessible to the outside aka SDasherWidget
-		virtual void SetScreen(SDasherWidget* screen);
+		virtual void SetScreen(CDasherScreen* screen);
 		//Sets the Training Filename
 		virtual void ImportTrainingFile(std::string filename);
 
diff --git a/Source/DasherVR/Public/DasherVR.h b/Source/DasherVR/Public/DasherVR.h
index 5aa2e2597631e06f77ab8e30ca5af57361e6da16..8a2bcca67e14c89f4d9b16b6196193fb26aa6964 100644
--- a/Source/DasherVR/Public/DasherVR.h
+++ b/Source/DasherVR/Public/DasherVR.h
@@ -3,6 +3,7 @@
 
 #include "CoreMinimal.h"
 #include "DasherCoreWrapper.h"
+#include "Modules/ModuleInterface.h"
 
 class FDasherVRModule : public IModuleInterface
 {
diff --git a/Source/DasherVR/Public/SDasherWidget.h b/Source/DasherVR/Public/SDasherWidget.h
index 31c993f819e6d1ddbdea130627718ba11dd8855c..b3e125514a7cf7e7315947b8cd992c54ec692e22 100644
--- a/Source/DasherVR/Public/SDasherWidget.h
+++ b/Source/DasherVR/Public/SDasherWidget.h
@@ -7,18 +7,26 @@
 #include "DasherInterface.h"
 #include "Math/Vector2D.h"
 #include "Fonts/FontMeasure.h"
+#include "Widgets/DeclarativeSyntaxSupport.h"
+#include "Cluster/IDisplayClusterClusterManager.h"
 
 //using namespace Dasher;
 
+//Structs to hold the elements making up the UI
 
+enum class GeometryType : uint8
+{
 
-//Structs to hold the elements making up the UI
+	Rectangle = 0,
+	Writing = 1,
+	PolyLine = 2
+};
 
-enum GeometryType
+enum class DasherEventType : uint8
 {
-	Rectangle,
-	Writing,
-	PolyLine
+	Input_MouseDown = 1,
+	Input_MouseUp = 2,
+	Tick = 3
 };
 
 struct DasherDrawGeometry
@@ -28,40 +36,51 @@ struct DasherDrawGeometry
 	virtual ~DasherDrawGeometry() {};
 };
 
-struct FFilledRect : DasherDrawGeometry{
+struct FFilledRect : DasherDrawGeometry {
 	FVector2D top;
 	FVector2D bottom;
 	FLinearColor color;
 
-	FFilledRect(FVector2D Top, FVector2D Bottom, FLinearColor Color) : DasherDrawGeometry(Rectangle), top(Top), bottom(Bottom), color(Color) {}
+	FFilledRect(FVector2D Top, FVector2D Bottom, FLinearColor Color) : DasherDrawGeometry(GeometryType::Rectangle), top(Top), bottom(Bottom), color(Color) {}
 };
 
 struct FWriting : DasherDrawGeometry{
-	Dasher::CDasherScreen::Label *label;
+	FString label;
 	FVector2D pos;
 	int size;
 	FLinearColor color;
-
-	FWriting(Dasher::CDasherScreen::Label *Label, FVector2D Pos, int Size, FLinearColor Color) : DasherDrawGeometry(Writing), label(Label), pos(Pos), size(Size), color(Color)  {}
+	FWriting(FString Label, FVector2D Pos, int Size, FLinearColor Color) : DasherDrawGeometry(GeometryType::Writing), label(Label), pos(Pos), size(Size), color(Color)  {}
 };
 
 struct FPolyLine : DasherDrawGeometry{
 	TArray<FVector2D> points;
 	float linewidth;
+	bool AntiAliasing;
 	FLinearColor color;
 
-	FPolyLine(TArray<FVector2D> Points, float LineWidth, FLinearColor Color): DasherDrawGeometry(PolyLine), points(Points), linewidth(LineWidth), color(Color)  {}
+	FPolyLine(TArray<FVector2D> Points, float LineWidth, FLinearColor Color, bool AntiAliasing): DasherDrawGeometry(GeometryType::PolyLine), points(Points), linewidth(LineWidth), AntiAliasing(AntiAliasing), color(Color)  {}
+};
+
+struct FMouseState
+{
+	bool PrimaryButtonPressed = false;
+	bool SecondaryButtonPressed = false;
+	bool DisabledDueToMouseLeave = false;
+	FVector2D CursorPosition = {0,0};
+	bool CurrentlyHovering = true;
 };
 
-DECLARE_DELEGATE(FDasherMouseUpDelegate);
-DECLARE_DELEGATE(FDasherMouseDownDelegate);
-DECLARE_DELEGATE_TwoParams(FBufferManiputlationDelegate, FString, FString);
+DECLARE_DELEGATE(FDasherEventDelegate);
+DECLARE_DELEGATE_TwoParams(FDasherMouseDelegate, int, bool);
+DECLARE_DELEGATE_OneParam(FBufferManiputlationDelegate, FString);
+DECLARE_DELEGATE_TwoParams(FCharManiputlationDelegate, FString, FString);
 
 
 class DASHERVR_API SDasherWidget : public SCompoundWidget, public Dasher::CDasherScreen, public Dasher::CScreenCoordInput
 {
 public:
 
+
 	SLATE_BEGIN_ARGS(SDasherWidget)
 	{}
 	
@@ -73,40 +92,34 @@ public:
 
 	typedef Dasher::screenint screenint;
 	
-	SDasherWidget(): CDasherScreen(0,0), CScreenCoordInput(0, _("Mouse Input")) {}
+	SDasherWidget(): CDasherScreen(0,0), CScreenCoordInput("Mouse Input") {}
 
 	// Constructs this widget with InArgs. Needed for every widget. Builds this widget and any of its children
 	void Construct(const FArguments& InArgs);
-	void SetBoolParamter(FString ParameterName, bool Value);
-	void SetLongParamter(FString ParameterName, int64 Value);
-	void SetStringParamter(FString ParameterName, FString Value);
+	void SetParameter(FString& ParameterName, bool Value);
+	void SetParameter(FString& ParameterName, int64 Value);
+	void SetParameter(FString& ParameterName, FString Value);
 
 	virtual int32 OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override;
 
 	//set of functions inherited from Dasherscreen used for displaying, see DasherScreen for further info
 
-	virtual void SetColourScheme(const Dasher::CColourIO::ColourInfo* pcolours) override;
-
     virtual std::pair<screenint, screenint> TextSize(CDasherScreen::Label* Label, unsigned int Size) override;
 
-	virtual void DrawString(CDasherScreen::Label *label, screenint x1, screenint y1, unsigned int size, int colour) override;
+	virtual void DrawString(CDasherScreen::Label *label, screenint x1, screenint y1, unsigned int size, const Dasher::ColorPalette::Color& color) override;
 
-	virtual void DrawRectangle(Dasher::screenint x1, Dasher::screenint y1, Dasher::screenint x2, Dasher::screenint y2, int colour, int ioutlinecolour, int ithickness) override;
+	virtual void DrawRectangle(Dasher::screenint x1, Dasher::screenint y1, Dasher::screenint x2, Dasher::screenint y2, const Dasher::ColorPalette::Color& color, const Dasher::ColorPalette::Color& outlinecolor, int ithickness) override;
 
-	virtual void DrawCircle(screenint icx, screenint icy, screenint ir, int ifillcolour, int ilinecolour, int ithickness) override {} //we don't really need to draw circles, so it's not implemented
+	virtual void DrawCircle(screenint icx, screenint icy, screenint ir, const Dasher::ColorPalette::Color& ifillcolor, const Dasher::ColorPalette::Color& ilinecolor, int ithickness) override {} //we don't really need to draw circles, so it's not implemented
 
-	virtual void Polyline(CDasherScreen::point* points, int number, int iwidth, int colour) override;
+	virtual void Polyline(CDasherScreen::point* points, int number, int iwidth, const Dasher::ColorPalette::Color& color) override;
 
-	virtual void Polygon(CDasherScreen::point* points, int number, int fillcolour, int outlinecolour, int iwidth) override;
+	virtual void Polygon(CDasherScreen::point* points, int number, const Dasher::ColorPalette::Color& fillcolor, const Dasher::ColorPalette::Color& outlinecolor, int iwidth) override;
 
 	virtual void Display() override;
 
-	virtual void SendMarker(int imarker) override {}
-
 	virtual bool IsPointVisible(screenint x, screenint y) override { return true; }
 
-	virtual bool MultiSizeFonts() override {return true;}
-
 	//Pass-me-down returning Buffer
 	FString GetBuffer() const;
 	void ResetBuffer();
@@ -114,29 +127,41 @@ public:
 	void StartTraining(FString PathToTextFile);
 
 	//Tick function inherited from SCompoundWidget
-	virtual void Tick(const FGeometry& AllotedGeometry, const double InCurrentTime, const float InDeltaTime) override;
+	virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override;
 
 	//Function to set if the widget is in editor or not
 	void SetEditor(bool EditorState);
 
 	//mouse handling function
-	FReply HandleMouseMoveEvent(const FGeometry& Geometry, const FPointerEvent& MouseEvent);
-	FReply HandleMouseDownEvent(const FGeometry& Geometry, const FPointerEvent& MouseEvent);
-	FReply HandleMouseUpEvent(const FGeometry& Geometry, const FPointerEvent& MouseEvent);
-    FReply HandleMouseDoubleClickEvent(const FGeometry& Geometry, const FPointerEvent& MouseEvent);
+	virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
+    virtual FReply OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
+    virtual void OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
+    virtual void OnMouseLeave(const FPointerEvent& MouseEvent) override;
+    virtual FReply OnMouseButtonDoubleClick(const FGeometry& InMyGeometry, const FPointerEvent& InMouseEvent) override;
+	virtual FReply OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
 
-	virtual bool SupportsKeyboardFocus() const override {return true;};
+	virtual bool SupportsKeyboardFocus() const override {return true;}
 
 	virtual bool GetScreenCoords(screenint& iX, screenint& iY, Dasher::CDasherView* pView) override;
 
 	void InputVector(FVector2D InputVector);
-	void InputButton(bool Pressed);
-	FVector2D GetCursorPosition();
-	
-	FDasherMouseUpDelegate MouseUpListeners;
-	FDasherMouseUpDelegate MouseDownListeners;
-	FBufferManiputlationDelegate CharacterEntered;
-	FBufferManiputlationDelegate CharacterDeleted;
+	void VectorInputButton(bool Pressed);
+	void VectorBoostButton(bool Pressed);
+	FVector2D GetCursorPosition() const;
+
+	//Allows to Pause Input
+	void EnableInput(bool enable);
+	bool GetInputEnabled() const;
+	FBufferManiputlationDelegate CharacterSwitched;
+	FDasherMouseDelegate MouseListeners;
+	FDasherMouseDelegate BoostListeners;
+	FCharManiputlationDelegate CharacterEntered;
+	FCharManiputlationDelegate CharacterDeleted;
+	FDasherEventDelegate CharacterEnteredLastFrame;
+	FDasherEventDelegate CharacterDeletedLastFrame;
+	FDasherEventDelegate CharacterSwitchedLastFrame;
+	FBufferManiputlationDelegate BufferAltered;
+	FDasherEventDelegate DasherFrameCompleted;
 
 private:
 	
@@ -146,21 +171,35 @@ private:
 	TArray<TUniquePtr<DasherDrawGeometry>>* BackBuffer = &GeometryBufferA;
 	TArray<TUniquePtr<DasherDrawGeometry>>* FrontBuffer = &GeometryBufferB;
 	
-	int Height;
-	int Width;
-	bool HasBeenPainted = false;
-	bool CurrentlyUsingVectorInput = false;
-	FVector2D CursorPosition;
+	int Height = 0;
+	int Width = 0;
 
-	//are we in the Editor?
-	bool IsEditor = true;
+	bool CurrentlyUsingVectorInput = false;
+	point CursorPosition = {0,0};
+	point NewMousePosition = {0,0};
+
+	bool IsEditor = true; //are we in the Editor?
+	bool CharDeletedInFrame = false;
+	bool CharEnteredInFrame = false;
+	bool InputPaused = false; //is the input paused
+
+	//Mouse States
+    TMap<int, FMouseState> MouseStates;
+	TArray<int> MouseInteractions;
+	const int VectorInputPointerIndex = 42;
+	FMouseState& GetMouseState(int Index);
+    void UpdateMouseInteraction(int Index);
+    int GetLastActiveMouseInteraction() const;
 
 	//set up the font measure service to ... measure fonts.
 	TSharedPtr<FSlateFontMeasure> FontMeasureService;
 
+	//CAVE Sync
+	FOnClusterEventBinaryListener ClusterEventListener;
+	inline static int32 DasherEventID = 424240;
+
 protected:
 	// stores color information 
-	const Dasher::CColourIO::ColourInfo* ColorPalette = nullptr;
 	TSharedPtr<Dasher::DasherInterface> DasherMainInterface;
 };
 
diff --git a/Source/DasherVR/Public/UDasherWidget.h b/Source/DasherVR/Public/UDasherWidget.h
index ae0cb7aecbcd3251e6438442002f72955648f02d..aa56bf36935d096709b7a384e8dedd79d66c991e 100644
--- a/Source/DasherVR/Public/UDasherWidget.h
+++ b/Source/DasherVR/Public/UDasherWidget.h
@@ -14,7 +14,8 @@
 
 DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FCharManipulatedEvent, FString, Char, FString, Buffer);
 DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FBufferManipulatedEvent, FString, Buffer);
-DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMouseEvent, bool, IsMouseDown);
+DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FMouseEvent, int, PointerIndex, bool, IsMouseDown);
+DECLARE_DYNAMIC_MULTICAST_DELEGATE(FDasherEvent);
 
 UCLASS(BlueprintType)
 class DASHERVR_API UDasherWidget : public UWidget
@@ -40,11 +41,19 @@ public:
 	UFUNCTION(BlueprintCallable) void InputButton(bool Pressed);
 	UFUNCTION(BlueprintCallable) void InputVector(FVector2D InputVector);
 
+	UFUNCTION(BlueprintPure) bool GetInputEnabled() const;
+	UFUNCTION(BlueprintCallable) void EnableInput(bool enabled);
+
 	UPROPERTY(BlueprintAssignable) FCharManipulatedEvent CharacterEntered;
 	UPROPERTY(BlueprintAssignable) FCharManipulatedEvent CharacterDeleted;
+	UPROPERTY(BlueprintAssignable) FCharManipulatedEvent CharacterSwitched;
 	UPROPERTY(BlueprintAssignable) FBufferManipulatedEvent BufferAltered;
 	UPROPERTY(BlueprintAssignable) FMouseEvent MouseEvent;
-
+	UPROPERTY(BlueprintAssignable) FMouseEvent BoostEvent;
+	UPROPERTY(BlueprintAssignable) FDasherEvent DasherFrameCompleted;
+	UPROPERTY(BlueprintAssignable) FDasherEvent CharacterSwitchedLastFrame;
+	UPROPERTY(BlueprintAssignable) FDasherEvent CharacterEnteredLastFrame;
+	UPROPERTY(BlueprintAssignable) FDasherEvent CharacterDeletedLastFrame;
 public:
 
 	// UWidget interface
diff --git a/Source/DasherVR/Public/pch_plugin.h b/Source/DasherVR/Public/pch_plugin.h
new file mode 100644
index 0000000000000000000000000000000000000000..6f70f09beec2219624baeca92e2cd7deaa104fb4
--- /dev/null
+++ b/Source/DasherVR/Public/pch_plugin.h
@@ -0,0 +1 @@
+#pragma once
diff --git a/Source/Thirdparty/CMakeLists.txt b/Source/Thirdparty/CMakeLists.txt
index 4c001d32bb46711687653f3ba04b89fd51750962..dc66fff409921184fb5c97aea50af2e039d4f665 100644
--- a/Source/Thirdparty/CMakeLists.txt
+++ b/Source/Thirdparty/CMakeLists.txt
@@ -14,12 +14,11 @@ project("DasherLib")
 # If using visual studio, make sure to select the release configuration if it is not your default
 ############################################################################################################
 
-
 ############################################################################################################
 # Set this to your libc++ path included with Unreal
 ############################################################################################################
-set(LIBC_PATH "/home/unreal/UE4_Build_426/Engine/Source/ThirdParty/Linux/LibCxx/")
-
+# set(LIBC_PATH "/work/vrdemo/unreal_engines/5.3.2/Engine/Source/ThirdParty/Unix/LibCxx/")
+set(LIBC_PATH "/home/ue4/UnrealEngine/Engine/Source/ThirdParty/Unix/LibCxx/")
 
 if (UNIX)
 	############################################################################################################
@@ -28,9 +27,9 @@ if (UNIX)
 
 	set(CMAKE_CXX_STANDARD 17)
 	set(CMAKE_CXX_STANDARD_REQUIRED ON)
-	add_compile_definitions(HAVE_ROUND XML_STATIC _CRT_SECURE_NO_WARNINGS HAVE_OWN_FILEUTILS HAVE_OWN_FILELOGGER)
+	add_compile_definitions(HAVE_ROUND XML_STATIC _CRT_SECURE_NO_WARNINGS HAVE_OWN_FILEUTILS HAVE_OWN_FILELOGGER PUGIXML_NO_EXCEPTIONS)
 	set(CMAKE_SUPPRESS_REGENERATION true)
-	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -std=c++17 -stdlib=libc++ -nostdinc++ -I${LIBC_PATH}include/c++/v1 -L${LIBC_PATH}lib -Wl -rpath ${LIBC_PATH}lib")
+	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -std=c++17 -nostdinc++ -fno-exceptions -I${LIBC_PATH}include/c++/v1 -L${LIBC_PATH}lib ${LIBC_PATH}lib")
 	set(CMAKE_EXE_LINKER_FLAGS "-stdlib=libc++")
 
 
diff --git a/Source/Thirdparty/Dasher/Dasher.Build.cs b/Source/Thirdparty/Dasher/Dasher.Build.cs
index fbaf4ffc6ef0f8aa75057032b7366e932ea0025b..c931821c7ff76d307f07cb8e5ec5ea377c4d2af4 100644
--- a/Source/Thirdparty/Dasher/Dasher.Build.cs
+++ b/Source/Thirdparty/Dasher/Dasher.Build.cs
@@ -8,7 +8,8 @@ public class Dasher : ModuleRules
 {
 	public Dasher(ReadOnlyTargetRules Target) : base(Target)
 	{
-		PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
+		PCHUsage = PCHUsageMode.NoSharedPCHs;
+		CppStandard = CppStandardVersion.Cpp17;
 		Type = ModuleType.External;
 
 		PrivateDefinitions.Add("HAVE_OWN_FILEUTILS");
@@ -47,6 +48,7 @@ public class Dasher : ModuleRules
 		if (Target.Platform == UnrealTargetPlatform.Linux)
 		{
 			PublicAdditionalLibraries.Add(Path.Combine(ModuleDirectory, "Lib", "DasherCore.a"));
+			PublicAdditionalLibraries.Add(Path.Combine(ModuleDirectory, "Lib", "libpugixml.a"));
 		}
 	}
 }
diff --git a/Source/Thirdparty/Dasher/DasherCore b/Source/Thirdparty/Dasher/DasherCore
index a94a47badf40d9b7f7e8c80c08b015c41d1ecc90..e654a32606dd360cafdb8956971fc94653f4c88d 160000
--- a/Source/Thirdparty/Dasher/DasherCore
+++ b/Source/Thirdparty/Dasher/DasherCore
@@ -1 +1 @@
-Subproject commit a94a47badf40d9b7f7e8c80c08b015c41d1ecc90
+Subproject commit e654a32606dd360cafdb8956971fc94653f4c88d
diff --git a/Source/Thirdparty/Dasher/Lib/DasherCore.a b/Source/Thirdparty/Dasher/Lib/DasherCore.a
new file mode 100644
index 0000000000000000000000000000000000000000..07b54651cd309089dcfdcdd9827ae3f0a41af66c
Binary files /dev/null and b/Source/Thirdparty/Dasher/Lib/DasherCore.a differ
diff --git a/Source/Thirdparty/Dasher/Lib/DasherCore.lib b/Source/Thirdparty/Dasher/Lib/DasherCore.lib
index 649532f6b3811d45e73e539ecfa9f151629881ba..e84f68856171851b4922525037191d04a76ba225 100644
--- a/Source/Thirdparty/Dasher/Lib/DasherCore.lib
+++ b/Source/Thirdparty/Dasher/Lib/DasherCore.lib
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:d51488c01a9dd197c65a23d33c65ff5f7f0cbf9853d0f9d634fa31423f90d56c
-size 17686042
+oid sha256:4d560e7ca59bee8fdc05b7178c51579f6ab13bee39256280e222527dddb84058
+size 29899184
diff --git a/Source/Thirdparty/Dasher/Lib/DasherCore.pdb b/Source/Thirdparty/Dasher/Lib/DasherCore.pdb
index f6044a85dcead2424134c4c7695360f57f29db1e..aa3548dbae81aa4ab322b860d0cb0c2e0746dd44 100644
Binary files a/Source/Thirdparty/Dasher/Lib/DasherCore.pdb and b/Source/Thirdparty/Dasher/Lib/DasherCore.pdb differ
diff --git a/Source/Thirdparty/Dasher/Lib/pugixml.lib b/Source/Thirdparty/Dasher/Lib/pugixml.lib
index cce3a4936cd35ce16565522cefeb54f99f96efc2..d70b5dc2d5a1a70e5232ca233b7ae60cf56c73d5 100644
--- a/Source/Thirdparty/Dasher/Lib/pugixml.lib
+++ b/Source/Thirdparty/Dasher/Lib/pugixml.lib
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:af6d87774854f597e9a3dc390060d76ab870e9dde54b5935d8029e336ac2eed2
-size 1463018
+oid sha256:9d1e2128f0f0e9423efe3d44823bd7e9768ee34aff6fb0f47ab6ac587a0bd973
+size 1502654
diff --git a/Source/Thirdparty/Dasher/Lib/pugixml.pdb b/Source/Thirdparty/Dasher/Lib/pugixml.pdb
index dc52d69d6d2a95beb29cb8419f2dadf1eb8b7be3..404bfd8c649e178052d3448e8a93cb460c3ba55b 100644
Binary files a/Source/Thirdparty/Dasher/Lib/pugixml.pdb and b/Source/Thirdparty/Dasher/Lib/pugixml.pdb differ
diff --git a/Source/Thirdparty/docker-compose.yml b/Source/Thirdparty/docker-compose.yml
new file mode 100644
index 0000000000000000000000000000000000000000..8948afe06a22cda17a26c3c2372efba7a5e0fde8
--- /dev/null
+++ b/Source/Thirdparty/docker-compose.yml
@@ -0,0 +1,6 @@
+services:
+  builder:
+    image: ghcr.io/epicgames/unreal-engine:dev-5.3
+    volumes:
+      - ./:/build
+    command: bash -c "sudo apt-get update && sudo apt-get install -y cmake && cd /build && rm -rf build && mkdir build && cd build && cmake .. && make"
\ No newline at end of file