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/Source/DasherVR/Private/SDasherWidget.cpp b/Source/DasherVR/Private/SDasherWidget.cpp
index 4b3e21678324f734d1a86d606e5132ccce7c260b..1d48f4b1c5fcea8f6a9809c9d6733a664389aea2 100644
--- a/Source/DasherVR/Private/SDasherWidget.cpp
+++ b/Source/DasherVR/Private/SDasherWidget.cpp
@@ -4,11 +4,6 @@
 #include "Brushes/SlateColorBrush.h"
 #include "Rendering/DrawElements.h"
 #include "DasherInterface.h"
-#include "IDisplayCluster.h"
-#include "Cluster/DisplayClusterClusterEvent.h"
-#include "Cluster/IDisplayClusterClusterManager.h"
-
-#include "Utility/RWTHVRUtilities.h"
 
 #include "Components/SlateWrapperTypes.h"
 #include "Framework/Application/SlateApplication.h"
@@ -22,331 +17,401 @@ BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
 //Set the state if we're in the editor or not.
 void SDasherWidget::SetEditor(bool EditorState)
 {
-    IsEditor = EditorState;
+	IsEditor = EditorState;
 }
-//Pause the input
-void SDasherWidget::PauseInput()
+
+void SDasherWidget::EnableInput(bool enable)
 {
-    if (!URWTHVRUtilities::IsPrimaryNode()) return;
-    InputPaused = true;
+	InputPaused = !enable;
 }
-//Unpause the input
-void SDasherWidget::UnpauseInput()
+
+bool SDasherWidget::GetInputEnabled() const
 {
-    if (!URWTHVRUtilities::IsPrimaryNode()) return;
-    InputPaused = false;
+	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)
-    {
-        if (CurrentlyUsingVectorInput) return FReply::Unhandled();
-        //The mouse event only contains the Screen Space Position
-        const FVector2D newMousePosition = Geometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition());
-
-		NewMousePosition.x = newMousePosition.X;
-		NewMousePosition.y = newMousePosition.Y;
-    }
-    return FReply::Handled();
+    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();
 }
 
-FReply SDasherWidget::HandleMouseDownEvent(const FGeometry& Geometry, const FPointerEvent& MouseEvent)
+FMouseState& SDasherWidget::GetMouseState(int Index)
 {
-    if (!InputPaused)
-    {
-        if(URWTHVRUtilities::IsRoomMountedMode())
-	    {
-            FDisplayClusterClusterEventBinary ClusterEvent;
-	        ClusterEvent.EventId = DasherEventID + static_cast<uint8>(DasherEventType::Input_MouseDown);
-		    IDisplayCluster::Get().GetClusterMgr()->EmitClusterEventBinary(ClusterEvent, false);
-            UE_LOG(LogTemp, Log, TEXT("MouseDownSend"));
-	        return FReply::Handled().LockMouseToWidget(AsShared());
-	    }
+    if(!MouseStates.Contains(Index)) MouseStates.Add(Index, {});
+    return MouseStates[Index];
+}
 
-        if (CurrentlyUsingVectorInput)
-        {
-            CurrentlyUsingVectorInput = false;
-            return FReply::Handled().LockMouseToWidget(AsShared());
-        }
-        DasherMainInterface->KeyDown(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Primary_Input);
-        MouseDownListeners.ExecuteIfBound();
-    }
-    return FReply::Handled().LockMouseToWidget(AsShared());
+// Moves Item to Back
+void SDasherWidget::UpdateMouseInteraction(int Index)
+{
+    MouseInteractions.RemoveSingle(Index);
+	MouseInteractions.Add(Index);
 }
 
-FReply SDasherWidget::HandleMouseUpEvent(const FGeometry& Geometry, const FPointerEvent& MouseEvent)
+int SDasherWidget::GetLastActiveMouseInteraction() const
 {
-    if(URWTHVRUtilities::IsRoomMountedMode())
+	// Find Mouse that already interacted
+    for(int i = MouseInteractions.Num() - 1; i >= 0; i--)
     {
-	    FDisplayClusterClusterEventBinary ClusterEvent;
-        ClusterEvent.EventId = ClusterEvent.EventId = DasherEventID + static_cast<uint8>(DasherEventType::Input_MouseUp);
-	    IDisplayCluster::Get().GetClusterMgr()->EmitClusterEventBinary(ClusterEvent, false);
-        return FReply::Handled().ReleaseMouseLock();
+        if(MouseStates[MouseInteractions[i]].CurrentlyHovering) return MouseInteractions[i];
     }
 
-    if (CurrentlyUsingVectorInput)
-    {
-        CurrentlyUsingVectorInput = false;
-        return FReply::Handled().ReleaseMouseLock();
-    }
-    DasherMainInterface->KeyUp(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Primary_Input);
-    MouseUpListeners.ExecuteIfBound();
+	//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 FReply::Handled().ReleaseMouseLock();
+	return -1;
 }
 
-FReply SDasherWidget::HandleMouseDoubleClickEvent(const FGeometry& Geometry, const FPointerEvent& MouseEvent)
+FReply SDasherWidget::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
 {
+    if (InputPaused) return FReply::Unhandled();
 
-    if (!InputPaused)
-    {
-	    if(URWTHVRUtilities::IsRoomMountedMode())
+	FMouseState& MouseState = GetMouseState(MouseEvent.GetPointerIndex());
+
+	//The mouse event only contains the Screen Space Position
+	MouseState.CursorPosition = MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition());
+	MouseState.CurrentlyHovering = true; //ensure hover set
+
+	return FReply::Handled();
+}
+
+void SDasherWidget::OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
+{
+	SWidget::OnMouseEnter(MyGeometry, MouseEvent);
+
+	FMouseState& MouseState = GetMouseState(MouseEvent.GetPointerIndex());
+
+	//Reenable if button is still pressed
+	if(MouseState.DisabledDueToMouseLeave)
+	{
+	    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))
 	    {
-		    FDisplayClusterClusterEventBinary ClusterEvent;
-	        ClusterEvent.EventId = ClusterEvent.EventId = DasherEventID + static_cast<uint8>(DasherEventType::Input_MouseDown);
-		    IDisplayCluster::Get().GetClusterMgr()->EmitClusterEventBinary(ClusterEvent, false);
-	        return FReply::Handled();
+	        DasherMainInterface->KeyDown(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Secondary_Input);
+		    UpdateMouseInteraction(MouseEvent.GetPointerIndex());
+		    BoostListeners.ExecuteIfBound(MouseEvent.GetPointerIndex(), true);
 	    }
+	    MouseState.DisabledDueToMouseLeave = false;
+	}
+}
 
-        if (CurrentlyUsingVectorInput)
-        {
-            CurrentlyUsingVectorInput = false;
-            return FReply::Handled();
-        }
-        DasherMainInterface->KeyUp(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Primary_Input);
-    }
-    return FReply::Handled();
+void SDasherWidget::OnMouseLeave(const FPointerEvent& MouseEvent)
+{
+	SWidget::OnMouseLeave(MouseEvent);
+
+	FMouseState& MouseState = GetMouseState(MouseEvent.GetPointerIndex());
+	MouseState.CurrentlyHovering = false;
+
+	if (InputPaused) return;
+
+	if(MouseState.PrimaryButtonPressed)
+	{
+	    DasherMainInterface->KeyUp(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Primary_Input);
+		MouseListeners.ExecuteIfBound(MouseEvent.GetPointerIndex(), false);
+		MouseState.DisabledDueToMouseLeave = true;
+	}
+
+	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::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
+{
+	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)
+	{
+		DasherMainInterface->KeyUp(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Secondary_Input);
+		MouseState.SecondaryButtonPressed = false;
+		BoostListeners.ExecuteIfBound(MouseEvent.GetPointerIndex(), MouseState.SecondaryButtonPressed);
+	}
+
+	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() const
 {
-    return FVector2D(CursorPosition.x, CursorPosition.y);
+	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();
+	const FVector2D Position = GetCursorPosition();
 
-    iX = Position.X;
-    iY = Position.Y;
+	iX = Position.X;
+	iY = Position.Y;
 
-    return true;
+	return true;
 }
 
-void SDasherWidget::HandleClusterEvent(const FDisplayClusterClusterEventBinary& Event)
+void SDasherWidget::InputVector(const FVector2D InputVector)
 {
-    switch(static_cast<DasherEventType>(Event.EventId - DasherEventID))
-    {
-    case DasherEventType::Input_MouseDown:
-        DasherMainInterface->KeyDown(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Primary_Input);
-		MouseDownListeners.ExecuteIfBound();
-        UE_LOG(LogTemp, Log, TEXT("MouseDownReceived"));
-	    break;
-    case DasherEventType::Input_MouseUp:
-        DasherMainInterface->KeyUp(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Primary_Input);
-		MouseUpListeners.ExecuteIfBound();   
-	    break;
-    case DasherEventType::Tick:
-        double currentTime = 0;
-
-    	FMemory::Memcpy(&currentTime, Event.EventData.GetData(), sizeof(currentTime));
-        FMemory::Memcpy(&CursorPosition.x, Event.EventData.GetData() + sizeof(currentTime), sizeof(CursorPosition.x));
-        FMemory::Memcpy(&CursorPosition.y, Event.EventData.GetData() + sizeof(currentTime) + sizeof(CursorPosition.x), sizeof(CursorPosition.y));
-
-        //DasherMainInterface->Tick(static_cast<unsigned long>(currentTime * 1000.0));
-	    break;
-    }
+	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::InputVector(const FVector2D InputVector)
+void SDasherWidget::VectorInputButton(bool Pressed)
 {
-    if (!InputPaused) {
-        if (!CurrentlyUsingVectorInput) return;
-        const FVector2D pos = DasherMainInterface->ConvertDasher2Screen(Dasher::CDasherModel::ORIGIN_X, Dasher::CDasherModel::ORIGIN_Y)
-            + InputVector * FVector2D(1.0f, -1.0f) * (GetHeight() / 2);
-        CursorPosition.x = pos.X;
-        CursorPosition.y = pos.Y;
-    }
+	if (InputPaused) return;
+
+	FMouseState& MouseState = GetMouseState(VectorInputPointerIndex);
+
+	if (Pressed)
+	{
+		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(), Dasher::Keys::Primary_Input);
+		MouseState.PrimaryButtonPressed = false;
+	}
 }
 
-void SDasherWidget::InputButton(bool Pressed)
+void SDasherWidget::VectorBoostButton(bool Pressed)
 {
-    if (!InputPaused) {
-        CurrentlyUsingVectorInput = true;
-        if (Pressed)
-        {
-            DasherMainInterface->KeyDown(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Primary_Input);
-        }
-        else {
-            DasherMainInterface->KeyUp(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Primary_Input);
-        }
-    }
+	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;
-    resize(Width, Height);
-
-    //initialize the font measuring service.
-    FontMeasureService = FSlateApplication::Get().GetRenderer()->GetFontMeasureService();
-
-    //Setting up Dasher
-    static Dasher::XMLErrorDisplay display;
-    Dasher::XmlSettingsStore* Settings = new Dasher::XmlSettingsStore("Settings.xml", &display); //Gets deleted somewhere else
-    Settings->Load();
-    Settings->Save();
-    DasherMainInterface = MakeShared<Dasher::DasherInterface>(Settings);
-    DasherMainInterface->GetModuleManager()->RegisterInputDeviceModule(this, true);
-
-    DasherMainInterface->SetScreen(this);
-    DasherMainInterface->SetBuffer(0);
-
-    DasherMainInterface->SetCharEnteredCallback([this](FString Char, FString Buffer) {CharacterEnteredFlag = true; AlteredChar = Char; });
-    DasherMainInterface->SetCharDeletedCallback([this](FString Char, FString Buffer) {CharacterDeletedFlag = true; AlteredChar = Char; });
-
-    // Bind the cluster events that manage the door state.
-    IDisplayClusterClusterManager* ClusterManager = IDisplayCluster::Get().GetClusterMgr();
-    if (URWTHVRUtilities::IsRoomMountedMode() && ClusterManager && !ClusterEventListener.IsBound())
-    {
-        ClusterEventListener =
-            FOnClusterEventBinaryListener::CreateSP(this, &SDasherWidget::HandleClusterEvent);
-        ClusterManager->AddClusterEventBinaryListener(ClusterEventListener);
-    }
-
-    if (URWTHVRUtilities::IsPrimaryNode())
-    {
-	    //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.
-    }
+	//Initial resize, needed for setup
+	Width = InArgs._width;
+	Height = InArgs._height;
+	resize(Width, Height);
+
+	//initialize the font measuring service.
+	FontMeasureService = FSlateApplication::Get().GetRenderer()->GetFontMeasureService();
+
+	//Setting up Dasher
+	static Dasher::XMLErrorDisplay display;
+	Dasher::XmlSettingsStore* Settings = new Dasher::XmlSettingsStore("Settings.xml", &display); //Gets deleted somewhere else
+	Settings->Load();
+	Settings->Save();
+	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);
+		CharEnteredInFrame = true;
+		BufferAltered.ExecuteIfBound(Buffer);
+	});
+	DasherMainInterface->SetCharDeletedCallback([this](FString Char, FString Buffer)
+	{
+		CharacterDeleted.ExecuteIfBound(Char, Buffer);
+		CharDeletedInFrame = true;
+		BufferAltered.ExecuteIfBound(Buffer);
+	});
 }
 
-void SDasherWidget::SetParameter(FString& ParameterName, bool Value) {
-    DasherMainInterface->SetBoolParameter(Dasher::Settings::GetParameter(TCHAR_TO_UTF8(*ParameterName)).first, Value);
+void SDasherWidget::SetParameter(FString& ParameterName, bool Value)
+{
+	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(param->first, Value);
+			return;
+		}
 }
 
-void SDasherWidget::SetParameter(FString& ParameterName, int64 Value) {
-    DasherMainInterface->SetLongParameter(Dasher::Settings::GetParameter(TCHAR_TO_UTF8(*ParameterName)).first, Value);
+void SDasherWidget::SetParameter(FString& ParameterName, int64 Value)
+{
+	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(param->first, Value);
+			return;
+		}
 }
 
-void SDasherWidget::SetParameter(FString& ParameterName, FString Value) {
-    DasherMainInterface->SetStringParameter(Dasher::Settings::GetParameter(TCHAR_TO_UTF8(*ParameterName)).first, TCHAR_TO_UTF8(*Value));
+void SDasherWidget::SetParameter(FString& ParameterName, FString Value)
+{
+	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(param->first, TCHAR_TO_UTF8(*Value));
+			return;
+		}
 }
 
 //paints our stuff, then lets compoundwidget (super::) draw its stuff
+//This draws from bottom to top rectangles->lines->labels, this is enough for our use, but for more complex scenarios a proper layering system might have to be implemented
 int32 SDasherWidget::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
 {
-    ////this doesn't draw anything red, we just need a brush. Could probably find a better "empty" brush
-    auto MyBrush = FSlateColorBrush(FColor::Red);
-
-    FFilledRect* RectObject;
-    FWriting* WritingObject;
-    FPolyLine* LineObject;
-    FVector2D Size;
-    FString Text;
-    FSlateFontInfo Font;
-
-    TArray<TUniquePtr<DasherDrawGeometry>>& GeometryBuffer = *FrontBuffer;
-    for (TUniquePtr<DasherDrawGeometry>& GeneralObject : GeometryBuffer) {
-        switch (GeneralObject->Type)
-        {
-        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());
-            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))), WritingObject->label, 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, LineObject->AntiAliasing, LineObject->linewidth);
-            break;
-        default: break;
-        }
-    }
-
-    return SCompoundWidget::OnPaint(Args, AllottedGeometry, MyClippingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled); //call the parent onPaint
+	//this doesn't draw anything red, we just need a brush. Could probably find a better "empty" brush
+	auto MyBrush = FSlateColorBrush(FColor::Red);
+
+	FFilledRect* RectObject;
+	FWriting* WritingObject;
+	FPolyLine* LineObject;
+	FVector2D Size;
+	FString Text;
+	FSlateFontInfo Font;
+
+	TArray<TUniquePtr<DasherDrawGeometry>>& GeometryBuffer = *FrontBuffer;
+	for (TUniquePtr<DasherDrawGeometry>& GeneralObject : GeometryBuffer)
+	{
+		switch (GeneralObject->Type)
+		{
+		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
 }
 
 //The tick function, we tick dasher in it and update the screen size for dasher
-void SDasherWidget::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) {
-    SCompoundWidget::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
-
-    //don't tick in the editor
-    if (IsEditor || InputPaused) return;
-
-    //Needs to be updated in Tick due to event order in cluster mode
-    CursorPosition = NewMousePosition;
-
-    if(!URWTHVRUtilities::IsRoomMountedMode()){
-        DasherMainInterface->Tick(static_cast<unsigned long>(InCurrentTime * 1000.0)); //we need to provide ticks in milliseconds
-    }
-	else
-    if(URWTHVRUtilities::IsPrimaryNode())
-    {
-        FDisplayClusterClusterEventBinary ClusterEvent;
-        ClusterEvent.EventId = ClusterEvent.EventId = DasherEventID + static_cast<uint8>(DasherEventType::Tick);
-        ClusterEvent.EventData.SetNumUninitialized(sizeof(InCurrentTime) + sizeof(CursorPosition.x) + sizeof(CursorPosition.y));
-
-        FMemory::Memcpy(ClusterEvent.EventData.GetData(), &InCurrentTime, sizeof(InCurrentTime));
-        FMemory::Memcpy(ClusterEvent.EventData.GetData() + sizeof(InCurrentTime), &CursorPosition.x, sizeof(CursorPosition.x));
-	    FMemory::Memcpy(ClusterEvent.EventData.GetData() + sizeof(InCurrentTime) + sizeof(CursorPosition.x), &CursorPosition.y, sizeof(CursorPosition.y));
-
-	    IDisplayCluster::Get().GetClusterMgr()->EmitClusterEventBinary(ClusterEvent, false);
-    }
-
-    //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) {
-        Width = AbsoluteSize.X;
-        Height = AbsoluteSize.Y;
-        resize(Width, Height);
-        //tell dasher we resized the screen, but only if there's actually a screen
-        if (Width && Height) DasherMainInterface->ScreenResized(this);
-    }
+void SDasherWidget::Tick(const FGeometry& AllotedGeometry, const double InCurrentTime, const float InDeltaTime)
+{
+	//don't tick in the editor
+	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)
+		{
+			Width = AbsoluteSize.X;
+			Height = AbsoluteSize.Y;
+			resize(Width, Height);
+			//tell dasher we resized the screen, but only if there's actually a screen
+			if (Width && Height) DasherMainInterface->ScreenResized(this);
+		};
+	}
 }
 
-std::pair<Dasher::screenint, Dasher::screenint> SDasherWidget::TextSize(Label* Label, unsigned Size)
+std::pair<SDasherWidget::screenint, SDasherWidget::screenint> SDasherWidget::TextSize(CDasherScreen::Label* Label, unsigned Size)
 {
-    const FSlateFontInfo Font = FCoreStyle::GetDefaultFontStyle("Roboto", Size, FFontOutlineSettings::NoOutline); //get the font
-    const FVector2D TextSize = FontMeasureService->Measure(FString(UTF8_TO_TCHAR(Label->m_strText.c_str())), Font, 1); //get the real size of the text, using the fontmeasuring service
-    return { static_cast<screenint>(TextSize.X), static_cast<screenint>(TextSize.Y) };
+	const FSlateFontInfo Font = FCoreStyle::GetDefaultFontStyle("Roboto", Size, FFontOutlineSettings::NoOutline); //get the font
+	const FVector2D TextSize = FontMeasureService->Measure(FString(UTF8_TO_TCHAR(Label->m_strText.c_str())), Font, 1); //get the real size of the text, using the fontmeasuring service
+	return {static_cast<screenint>(TextSize.X), static_cast<screenint>(TextSize.Y)};
 }
 
 //Double Buffers are rotated here.
-void SDasherWidget::Display() {
-    std::swap(FrontBuffer, BackBuffer);
-    BackBuffer->Empty();
-
-    if (CharacterEnteredFlag && CharacterDeletedFlag) {
-        CharacterSwitched.ExecuteIfBound(AlteredChar, GetBuffer());
-    }
-    else if (CharacterEnteredFlag) {
-        CharacterEntered.ExecuteIfBound(AlteredChar, GetBuffer());
-    }
-    else if (CharacterDeletedFlag) {
-        CharacterDeleted.ExecuteIfBound(AlteredChar, GetBuffer());
-    }
-
-    CharacterEnteredFlag = false;
-    CharacterDeletedFlag = false;
+void SDasherWidget::Display()
+{
+	std::swap(FrontBuffer, BackBuffer);
+	BackBuffer->Empty();
+
+	if(CharEnteredInFrame && CharDeletedInFrame)
+	{
+		CharacterSwitchedLastFrame.ExecuteIfBound();
+	}
+	else if(CharEnteredInFrame)
+	{
+		CharacterEnteredLastFrame.ExecuteIfBound();
+	}
+	else if(CharDeletedInFrame)
+	{
+		CharacterDeletedLastFrame.ExecuteIfBound();
+	}
+
+	CharEnteredInFrame = false;
+	CharDeletedInFrame = false;
+
+	DasherFrameCompleted.ExecuteIfBound();
 }
 
+//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))
+
 //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)
 {
@@ -397,22 +462,24 @@ void SDasherWidget::Polygon(CDasherScreen::point* points, int number, const Dash
     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
 {
-    return DasherMainInterface->GetBuffer();
+	return DasherMainInterface->GetBuffer();
 }
 
 void SDasherWidget::ResetBuffer()
 {
-    DasherMainInterface->ResetBuffer();
+	DasherMainInterface->ResetBuffer();
 }
 
 void SDasherWidget::StartTraining(FString PathToTextFile)
 {
-    DasherMainInterface->ImportTrainingFile(TCHAR_TO_UTF8(*FPaths::ConvertRelativePathToFull(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 b5482574059a07465e738949d71e87d5c52e0c1d..ca9fef7ac5665402d28c5b7da55b73042a9a5e09 100644
--- a/Source/DasherVR/Private/UDasherWidget.cpp
+++ b/Source/DasherVR/Private/UDasherWidget.cpp
@@ -11,110 +11,105 @@
 UDasherWidget::UDasherWidget(const FObjectInitializer& ObjectInitializer)
   : Super(ObjectInitializer)
 {
-  //Default Values Set Here, see above
+	//Default Values Set Here, see above
 }
 
 //Rebuild using custom Slate Widget 
 TSharedRef<SWidget> UDasherWidget::RebuildWidget()
 {
-  if (!DasherScreen)
-  {
-    DasherScreen = SNew(SDasherWidget).height(1080).width(1920);
-  }
-  return DasherScreen.ToSharedRef();
+	if (!DasherScreen)
+	{
+		DasherScreen = SNew(SDasherWidget).height(1080).width(1920);
+	}
+	return DasherScreen.ToSharedRef();
 }
 
 void UDasherWidget::SynchronizeProperties()
 {
-  Super::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->CharacterSwitched.BindLambda([this](FString Char, FString Buffer)
-    {
-        CharacterSwitched.Broadcast(Char, Buffer);
-        BufferAltered.Broadcast(Buffer);
-    });
-  DasherScreen->MouseUpListeners.BindLambda([this]() {MouseEvent.Broadcast(false); });
-  DasherScreen->MouseDownListeners.BindLambda([this]() {MouseEvent.Broadcast(true); });
+	Super::SynchronizeProperties();
+
+	//Check if we're in Editor
+	DasherScreen->SetEditor(IsDesignTime());
+	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);
+	Super::ReleaseSlateResources(bReleaseChildren);
 
-  DasherScreen.Reset();
+	if(bReleaseChildren) DasherScreen.Reset();
 }
 
 FString UDasherWidget::GetBuffer()
 {
-  return DasherScreen->GetBuffer();
+	if(!DasherScreen) return "";
+	return DasherScreen->GetBuffer();
 }
 
 void UDasherWidget::ResetBuffer()
 {
-  DasherScreen->ResetBuffer();
-  BufferAltered.Broadcast("");
+	if(!DasherScreen) return;
+	DasherScreen->ResetBuffer();
+	BufferAltered.Broadcast("");
 }
 
 void UDasherWidget::StartTraining(FString PathToTextFile)
 {
-  DasherScreen->StartTraining(PathToTextFile);
+	if(!DasherScreen) return;
+	DasherScreen->StartTraining(PathToTextFile);
 }
 
 void UDasherWidget::SetBoolParamter(FString ParameterName, bool Value)
 {
-  DasherScreen->SetParameter(ParameterName, Value);
+	if(!DasherScreen) return;
+	DasherScreen->SetParameter(ParameterName, Value);
 }
 
 void UDasherWidget::SetLongParamter(FString ParameterName, int64 Value)
 {
-  DasherScreen->SetParameter(ParameterName, Value);
+	if(!DasherScreen) return;
+	DasherScreen->SetParameter(ParameterName, Value);
 }
 
 void UDasherWidget::SetStringParamter(FString ParameterName, FString Value)
 {
-  DasherScreen->SetParameter(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)
 {
+	if(!DasherScreen) return;
   DasherScreen->InputVector(InputVector);
 }
 
-void UDasherWidget::PauseInput()
+bool UDasherWidget::GetInputEnabled() const
 {
-    DasherScreen->PauseInput();
+	if(!DasherScreen) return false;
+	return DasherScreen->GetInputEnabled();
 }
 
-void UDasherWidget::UnpauseInput()
+void UDasherWidget::EnableInput(bool enabled)
 {
-    DasherScreen->UnpauseInput();
+	if(!DasherScreen) return;
+	DasherScreen->EnableInput(enabled);
 }
 
-
 #if WITH_EDITOR
-
-//const FSlateBrush* UDasherWidget::GetEditorIcon()
-//{
-//	return FUMGStyle::Get().GetBrush("Widget.Image");
-//}
-
 const FText UDasherWidget::GetPaletteCategory()
 {
   return LOCTEXT("Common", "Common");
@@ -125,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/SDasherWidget.h b/Source/DasherVR/Public/SDasherWidget.h
index 6b6f74603623baab4861e96fe86c1bbf468a4e0f..b3e125514a7cf7e7315947b8cd992c54ec692e22 100644
--- a/Source/DasherVR/Public/SDasherWidget.h
+++ b/Source/DasherVR/Public/SDasherWidget.h
@@ -16,6 +16,7 @@
 
 enum class GeometryType : uint8
 {
+
 	Rectangle = 0,
 	Writing = 1,
 	PolyLine = 2
@@ -48,7 +49,6 @@ struct FWriting : DasherDrawGeometry{
 	FVector2D pos;
 	int size;
 	FLinearColor color;
-
 	FWriting(FString Label, FVector2D Pos, int Size, FLinearColor Color) : DasherDrawGeometry(GeometryType::Writing), label(Label), pos(Pos), size(Size), color(Color)  {}
 };
 
@@ -61,15 +61,26 @@ struct FPolyLine : DasherDrawGeometry{
 	FPolyLine(TArray<FVector2D> Points, float LineWidth, FLinearColor Color, bool AntiAliasing): DasherDrawGeometry(GeometryType::PolyLine), points(Points), linewidth(LineWidth), AntiAliasing(AntiAliasing), color(Color)  {}
 };
 
-DECLARE_DELEGATE(FDasherMouseUpDelegate);
-DECLARE_DELEGATE(FDasherMouseDownDelegate);
-DECLARE_DELEGATE_TwoParams(FBufferManiputlationDelegate, FString, FString);
+struct FMouseState
+{
+	bool PrimaryButtonPressed = false;
+	bool SecondaryButtonPressed = false;
+	bool DisabledDueToMouseLeave = false;
+	FVector2D CursorPosition = {0,0};
+	bool CurrentlyHovering = true;
+};
+
+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)
 	{}
 	
@@ -122,30 +133,35 @@ public:
 	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 GetScreenCoords(screenint& iX, screenint& iY, Dasher::CDasherView* pView) override;
 
-	void HandleClusterEvent(const FDisplayClusterClusterEventBinary& Event);
-
 	void InputVector(FVector2D InputVector);
-	void InputButton(bool Pressed);
+	void VectorInputButton(bool Pressed);
+	void VectorBoostButton(bool Pressed);
 	FVector2D GetCursorPosition() const;
 
 	//Allows to Pause Input
-	void PauseInput();
-	void UnpauseInput();
-
-	FDasherMouseUpDelegate MouseUpListeners;
-	FDasherMouseUpDelegate MouseDownListeners;
-	FBufferManiputlationDelegate CharacterEntered;
-	FBufferManiputlationDelegate CharacterDeleted;
+	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:
 	
@@ -157,18 +173,23 @@ private:
 	
 	int Height = 0;
 	int Width = 0;
-	bool HasBeenPainted = false;
+
 	bool CurrentlyUsingVectorInput = false;
 	point CursorPosition = {0,0};
 	point NewMousePosition = {0,0};
-	bool CharacterEnteredFlag = false;
-	bool CharacterDeletedFlag = false;
-	FString AlteredChar = "";
-
-	//are we in the Editor?
-	bool IsEditor = true;
-	//is the input paused
-	bool InputPaused = false;
+
+	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;
diff --git a/Source/DasherVR/Public/UDasherWidget.h b/Source/DasherVR/Public/UDasherWidget.h
index 849b3f18dd56781b6d47fd2534552aae1ac997cb..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,15 +41,19 @@ public:
 	UFUNCTION(BlueprintCallable) void InputButton(bool Pressed);
 	UFUNCTION(BlueprintCallable) void InputVector(FVector2D InputVector);
 
-	UFUNCTION(BlueprintCallable) void PauseInput();
-	UFUNCTION(BlueprintCallable) void UnpauseInput();
+	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/Thirdparty/Dasher/Dasher.Build.cs b/Source/Thirdparty/Dasher/Dasher.Build.cs
index 9b52064c9bf9ddab1f234fe6fcb97f4c443960cb..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");
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