Skip to content
Snippets Groups Projects
Select Git revision
  • c32234fdd9a6b31d14db4ac45264c925482fb0ec
  • v2.0 default protected
  • main protected
3 results

bits_ir_receiver.h

Blame
  • SDasherWidget.cpp 19.11 KiB
    #include "SDasherWidget.h"
    #include "SlateOptMacros.h"
    #include "Styling/CoreStyle.h"
    #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"
    
    BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
    
    
    // ++ This is needed in order to use the localization macro LOCTEXT
    #define LOCTEXT_NAMESPACE "SStandardSlateWidget"
    
    //Set the state if we're in the editor or not.
    void SDasherWidget::SetEditor(bool EditorState)
    {
        IsEditor = EditorState;
    }
    //Pause the input
    void SDasherWidget::PauseInput()
    {
        if (!URWTHVRUtilities::IsPrimaryNode()) return;
        InputPaused = true;
    }
    //Unpause the input
    void SDasherWidget::UnpauseInput()
    {
        if (!URWTHVRUtilities::IsPrimaryNode()) return;
        InputPaused = false;
    }
    
    //Event Handlers
    //Mouse position saved for mouse Input
    FReply SDasherWidget::HandleMouseMoveEvent(const FGeometry& Geometry, 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();
    }
    
    FReply SDasherWidget::HandleMouseDownEvent(const FGeometry& Geometry, const FPointerEvent& MouseEvent)
    {
        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 (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());
    }
    
    FReply SDasherWidget::HandleMouseUpEvent(const FGeometry& Geometry, const FPointerEvent& MouseEvent)
    {
        if(URWTHVRUtilities::IsRoomMountedMode())
        {
    	    FDisplayClusterClusterEventBinary ClusterEvent;
            ClusterEvent.EventId = ClusterEvent.EventId = DasherEventID + static_cast<uint8>(DasherEventType::Input_MouseUp);
    	    IDisplayCluster::Get().GetClusterMgr()->EmitClusterEventBinary(ClusterEvent, false);
            return FReply::Handled().ReleaseMouseLock();
        }
    
        if (CurrentlyUsingVectorInput)
        {
            CurrentlyUsingVectorInput = false;
            return FReply::Handled().ReleaseMouseLock();
        }
        DasherMainInterface->KeyUp(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Primary_Input);
        MouseUpListeners.ExecuteIfBound();
    
        return FReply::Handled().ReleaseMouseLock();
    }
    
    FReply SDasherWidget::HandleMouseDoubleClickEvent(const FGeometry& Geometry, const FPointerEvent& MouseEvent)
    {
    
        if (!InputPaused)
        {
    	    if(URWTHVRUtilities::IsRoomMountedMode())
    	    {
    		    FDisplayClusterClusterEventBinary ClusterEvent;
    	        ClusterEvent.EventId = ClusterEvent.EventId = DasherEventID + static_cast<uint8>(DasherEventType::Input_MouseDown);
    		    IDisplayCluster::Get().GetClusterMgr()->EmitClusterEventBinary(ClusterEvent, false);
    	        return FReply::Handled();
    	    }
    
            if (CurrentlyUsingVectorInput)
            {
                CurrentlyUsingVectorInput = false;
                return FReply::Handled();
            }
            DasherMainInterface->KeyUp(FDateTime::Now().GetSecond() + FDateTime::Now().GetMillisecond(), Dasher::Keys::Primary_Input);
        }
        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);
    }
    
    bool SDasherWidget::GetScreenCoords(screenint& iX, screenint& iY, Dasher::CDasherView* pView)
    {
        const FVector2D Position = GetCursorPosition();
    
        iX = Position.X;
        iY = Position.Y;
    
        return true;
    }
    
    void SDasherWidget::HandleClusterEvent(const FDisplayClusterClusterEventBinary& Event)
    {
        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;
        }
    }
    
    void SDasherWidget::InputVector(const FVector2D InputVector)
    {
        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;
        }
    }
    
    void SDasherWidget::InputButton(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);
            }
        }
    }
    
    //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.
        }
    }
    
    void SDasherWidget::SetParameter(FString& ParameterName, bool Value) {
        DasherMainInterface->SetBoolParameter(Dasher::Settings::GetParameter(TCHAR_TO_UTF8(*ParameterName)).first, Value);
    }
    
    void SDasherWidget::SetParameter(FString& ParameterName, int64 Value) {
        DasherMainInterface->SetLongParameter(Dasher::Settings::GetParameter(TCHAR_TO_UTF8(*ParameterName)).first, Value);
    }
    
    void SDasherWidget::SetParameter(FString& ParameterName, FString Value) {
        DasherMainInterface->SetStringParameter(Dasher::Settings::GetParameter(TCHAR_TO_UTF8(*ParameterName)).first, TCHAR_TO_UTF8(*Value));
    }
    
    //paints our stuff, then lets compoundwidget (super::) draw its stuff
    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
    }
    
    //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);
        }
    }
    
    std::pair<Dasher::screenint, Dasher::screenint> SDasherWidget::TextSize(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) };
    }
    
    //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;
    }
    
    //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));
        }
    }
    
    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)));
    }
    
    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);
        }
    
        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));
    }
    
    //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
    {
        return DasherMainInterface->GetBuffer();
    }
    
    void SDasherWidget::ResetBuffer()
    {
        DasherMainInterface->ResetBuffer();
    }
    
    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
    
    END_SLATE_FUNCTION_BUILD_OPTIMIZATION