diff --git a/OSC/Source/OSC/OSC.Build.cs b/OSC/Source/OSC/OSC.Build.cs index 5dad55d..c23fa8e 100644 --- a/OSC/Source/OSC/OSC.Build.cs +++ b/OSC/Source/OSC/OSC.Build.cs @@ -17,6 +17,9 @@ namespace UnrealBuildTool.Rules "CoreUObject", "Engine", "Sockets", + "InputCore", + "Slate", + "SlateCore", } ); diff --git a/OSC/Source/OSC/Private/Common/OscDataElemStruct.h b/OSC/Source/OSC/Private/Common/OscDataElemStruct.h index bbcc4e0..c871d46 100644 --- a/OSC/Source/OSC/Private/Common/OscDataElemStruct.h +++ b/OSC/Source/OSC/Private/Common/OscDataElemStruct.h @@ -121,6 +121,7 @@ struct FOscDataElemStruct //------------------------------------------------------------------------- + /// Cast the value to T. template T GetValue() const { diff --git a/OSC/Source/OSC/Private/OscModule.cpp b/OSC/Source/OSC/Private/OscModule.cpp index 58018a5..5a3e4f5 100644 --- a/OSC/Source/OSC/Private/OscModule.cpp +++ b/OSC/Source/OSC/Private/OscModule.cpp @@ -61,8 +61,12 @@ public: virtual void ShutdownModule( ) override { UE_LOG(LogOSC, Display, TEXT("Shutdown")); + if(_dispatcher.IsValid()) { + auto settings = GetMutableDefault(); + settings->ClearKeyInputs(*_dispatcher); + _dispatcher->Stop(); } } @@ -94,6 +98,9 @@ public: // send settings settings->UpdateSendAddresses(); + // input settings + settings->UpdateKeyInputs(*_dispatcher); + return true; } diff --git a/OSC/Source/OSC/Private/OscSettings.cpp b/OSC/Source/OSC/Private/OscSettings.cpp index 4210b42..f25200e 100644 --- a/OSC/Source/OSC/Private/OscSettings.cpp +++ b/OSC/Source/OSC/Private/OscSettings.cpp @@ -1,5 +1,8 @@ #include "OscPrivatePCH.h" +#include "OscReceiverInputKey.h" +#include "OscDispatcher.h" + UOscSettings::UOscSettings( const class FPostConstructInitializeProperties& PCIP ) : Super(PCIP), @@ -100,3 +103,24 @@ bool UOscSettings::Parse(const FString & address_port, FIPv4Address * address, u *port = portResult; return true; } + +void UOscSettings::ClearKeyInputs(UOscDispatcher & dispatcher) +{ + for(auto & receiver : _keyReceivers) + { + // Unregister here, not in the OscReceiverInputKey destructor + // because it would crash at the application exit. + dispatcher.UnregisterReceiver(&receiver); + } + _keyReceivers.Reset(0); +} + +void UOscSettings::UpdateKeyInputs(UOscDispatcher & dispatcher) +{ + ClearKeyInputs(dispatcher); + for(const auto & address : Inputs) + { + _keyReceivers.Emplace(address); + dispatcher.RegisterReceiver(&_keyReceivers.Last()); + } +} diff --git a/OSC/Source/OSC/Private/OscSettings.h b/OSC/Source/OSC/Private/OscSettings.h index 9b48a90..ac85d04 100644 --- a/OSC/Source/OSC/Private/OscSettings.h +++ b/OSC/Source/OSC/Private/OscSettings.h @@ -1,10 +1,11 @@ -// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved. - #pragma once #include #include "OscSettings.generated.h" +class OscReceiverInputKey; +class UOscDispatcher; + UCLASS(config=Engine) class UOscSettings : public UObject @@ -27,11 +28,24 @@ public: UPROPERTY(Config, EditAnywhere, Category=Send) TArray SendTargets; + /** + * List of the messages treated as inputs. + * + * Every entry adds a key in the input mapping project setting. + * e.g. "/position/x" -> "OSC_position_x" + */ + UPROPERTY(Config, EditAnywhere, Category=Input) + TArray Inputs; + public: void UpdateSendAddresses(); void Send(const uint8 *buffer, int32 length, int32 targetIndex); + void ClearKeyInputs(UOscDispatcher & dispatcher); + + void UpdateKeyInputs(UOscDispatcher & dispatcher); + public: /** @@ -43,4 +57,5 @@ public: private: TSharedRef _sendSocket; TArray>> _sendAddresses; + TArray _keyReceivers; }; diff --git a/OSC/Source/OSC/Private/Receive/OscDispatcher.cpp b/OSC/Source/OSC/Private/Receive/OscDispatcher.cpp index 2b95d66..8af82f0 100644 --- a/OSC/Source/OSC/Private/Receive/OscDispatcher.cpp +++ b/OSC/Source/OSC/Private/Receive/OscDispatcher.cpp @@ -70,11 +70,13 @@ void UOscDispatcher::Stop() void UOscDispatcher::RegisterReceiver(IOscReceiverInterface * receiver) { + FScopeLock ScopeLock(&_receiversMutex); _receivers.AddUnique(receiver); } void UOscDispatcher::UnregisterReceiver(IOscReceiverInterface * receiver) { + FScopeLock ScopeLock(&_receiversMutex); _receivers.Remove(receiver); } @@ -176,6 +178,8 @@ void UOscDispatcher::Callback(const FArrayReaderPtr& data, const FIPv4Endpoint&) void UOscDispatcher::CallbackMainThread() { + FScopeLock ScopeLock(&_receiversMutex); + std::pair> message; while(_pendingMessages.Dequeue(message)) { diff --git a/OSC/Source/OSC/Private/Receive/OscDispatcher.h b/OSC/Source/OSC/Private/Receive/OscDispatcher.h index 55e928d..1dd7ad1 100644 --- a/OSC/Source/OSC/Private/Receive/OscDispatcher.h +++ b/OSC/Source/OSC/Private/Receive/OscDispatcher.h @@ -3,7 +3,7 @@ #include "OscDataElemStruct.h" #include "OscDispatcher.generated.h" -class IOscReceiverInterface; +struct IOscReceiverInterface; class FSocket; class FUdpSocketReceiver; @@ -45,4 +45,7 @@ private: FSocket * _socket; FUdpSocketReceiver * _socketReceiver; TCircularQueue>> _pendingMessages; + + /// Protects _receivers + FCriticalSection _receiversMutex; }; diff --git a/OSC/Source/OSC/Private/Receive/OscReceiverActor.cpp b/OSC/Source/OSC/Private/Receive/OscReceiverActor.cpp index 2a12516..c0bc500 100644 --- a/OSC/Source/OSC/Private/Receive/OscReceiverActor.cpp +++ b/OSC/Source/OSC/Private/Receive/OscReceiverActor.cpp @@ -4,12 +4,13 @@ AOscReceiverActor::AOscReceiverActor(const class FPostConstructInitializeProperties& PCIP) - : Super(PCIP) + : Super(PCIP), + _listener(this) { auto instance = UOscDispatcher::Get(); if (instance && !HasAnyFlags(RF_ClassDefaultObject)) { - instance->RegisterReceiver(this); + instance->RegisterReceiver(&_listener); } } @@ -18,7 +19,7 @@ void AOscReceiverActor::BeginDestroy() auto instance = UOscDispatcher::Get(); if (instance && !HasAnyFlags(RF_ClassDefaultObject)) { - instance->UnregisterReceiver(this); + instance->UnregisterReceiver(&_listener); } Super::BeginDestroy(); diff --git a/OSC/Source/OSC/Private/Receive/OscReceiverActor.h b/OSC/Source/OSC/Private/Receive/OscReceiverActor.h index 5bda466..7123ad9 100644 --- a/OSC/Source/OSC/Private/Receive/OscReceiverActor.h +++ b/OSC/Source/OSC/Private/Receive/OscReceiverActor.h @@ -7,7 +7,7 @@ UCLASS() -class AOscReceiverActor : public AActor, public IOscReceiverInterface +class AOscReceiverActor : public AActor { GENERATED_UCLASS_BODY() @@ -17,17 +17,20 @@ class AOscReceiverActor : public AActor, public IOscReceiverInterface UFUNCTION(BlueprintImplementableEvent, Category=OSC) void OnOscReceived(const FName & Address, const TArray & Data); -protected: - - virtual void BeginDestroy() override; - - virtual const FString & GetAddressFilter() override +public: + const FString & GetAddressFilter() const { return AddressFilter; } - virtual void SendEvent(const FName & Address, const TArray & Data) override + void SendEvent(const FName & Address, const TArray & Data) { OnOscReceived(Address, Data); } + +private: + void BeginDestroy() override; + +private: + BasicOscReceiver _listener; }; diff --git a/OSC/Source/OSC/Private/Receive/OscReceiverComponent.cpp b/OSC/Source/OSC/Private/Receive/OscReceiverComponent.cpp index 50eba74..e6aae69 100644 --- a/OSC/Source/OSC/Private/Receive/OscReceiverComponent.cpp +++ b/OSC/Source/OSC/Private/Receive/OscReceiverComponent.cpp @@ -4,7 +4,8 @@ UOscReceiverComponent::UOscReceiverComponent(const class FPostConstructInitializeProperties& PCIP) - : Super(PCIP) + : Super(PCIP), + _listener(this) { } @@ -16,7 +17,7 @@ void UOscReceiverComponent::OnRegister() auto instance = UOscDispatcher::Get(); if (instance && !HasAnyFlags(RF_ClassDefaultObject)) { - instance->RegisterReceiver(this); + instance->RegisterReceiver(&_listener); } } @@ -25,7 +26,7 @@ void UOscReceiverComponent::OnUnregister() auto instance = UOscDispatcher::Get(); if (instance && !HasAnyFlags(RF_ClassDefaultObject)) { - instance->UnregisterReceiver(this); + instance->UnregisterReceiver(&_listener); } Super::OnUnregister(); diff --git a/OSC/Source/OSC/Private/Receive/OscReceiverComponent.h b/OSC/Source/OSC/Private/Receive/OscReceiverComponent.h index 27eea11..35dd2c9 100644 --- a/OSC/Source/OSC/Private/Receive/OscReceiverComponent.h +++ b/OSC/Source/OSC/Private/Receive/OscReceiverComponent.h @@ -11,7 +11,7 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FComponentOscReceivedSignature, con UCLASS(meta = (BlueprintSpawnableComponent)) -class UOscReceiverComponent : public UActorComponent, public IOscReceiverInterface +class UOscReceiverComponent : public UActorComponent { GENERATED_UCLASS_BODY() @@ -21,18 +21,22 @@ class UOscReceiverComponent : public UActorComponent, public IOscReceiverInterfa UPROPERTY(BlueprintAssignable, Category=OSC) FComponentOscReceivedSignature OnOscReceived; -protected: - virtual void OnRegister() override; - - virtual void OnUnregister() override; - - virtual const FString & GetAddressFilter() override +public: + const FString & GetAddressFilter() const { return AddressFilter; } - virtual void SendEvent(const FName & Address, const TArray & Data) override + void SendEvent(const FName & Address, const TArray & Data) { OnOscReceived.Broadcast(Address, Data); } + +private: + void OnRegister() override; + + void OnUnregister() override; + +private: + BasicOscReceiver _listener; }; diff --git a/OSC/Source/OSC/Private/Receive/OscReceiverInputKey.cpp b/OSC/Source/OSC/Private/Receive/OscReceiverInputKey.cpp new file mode 100644 index 0000000..c4f44cb --- /dev/null +++ b/OSC/Source/OSC/Private/Receive/OscReceiverInputKey.cpp @@ -0,0 +1,62 @@ +#include "OscPrivatePCH.h" +#include "OscReceiverInputKey.h" +#include "OscDispatcher.h" + +#include "SlateBasics.h" + + +OscReceiverInputKey::OscReceiverInputKey(const FString &address) + : _address(address), + _addressName(*address) +{ + char buffer[512]; // address truncated after 500 chars. + const auto length = sprintf(buffer, "OSC%.500s", StringCast(*address).Get()); + + // replace '/' by '_' because '/' is not a valid FKey character. + for(int i=0; i!=length; ++i) + { + if(buffer[i] == '/') + { + buffer[i] = '_'; + } + } + + _key = FName(buffer); + + RegisterKey(); +} + +void OscReceiverInputKey::RegisterKey() const +{ + if(!_address.IsEmpty() && !EKeys::GetKeyDetails(_key).IsValid()) + { + EKeys::AddKey(FKeyDetails(_key, + FText::FromString(_key.ToString()), + FKeyDetails::FloatAxis)); + } +} + +void OscReceiverInputKey::SendEvent(const FName & Address, const TArray & Data) +{ + if(Address != _addressName) + { + return; + } + + auto & app = FSlateApplication::Get(); + + const auto value = Data.Num() ? Data[0].GetValue() : 0.f; + + FKeyEvent keyEvent(_key, app.GetModifierKeys(), 0, false, 0, 0); + if(value < 0.5f) + { + app.ProcessKeyDownEvent(keyEvent); + } + else + { + app.ProcessKeyUpEvent(keyEvent); + } + + FAnalogInputEvent axisEvent(_key, app.GetModifierKeys(), 0, false, 0, 0, value); + app.ProcessAnalogInputEvent(axisEvent); +} diff --git a/OSC/Source/OSC/Private/Receive/OscReceiverInputKey.h b/OSC/Source/OSC/Private/Receive/OscReceiverInputKey.h new file mode 100644 index 0000000..e3bfc36 --- /dev/null +++ b/OSC/Source/OSC/Private/Receive/OscReceiverInputKey.h @@ -0,0 +1,33 @@ +#pragma once + +#include "InputCoreTypes.h" + +#include "OscReceiverInterface.h" +#include "OscDataElemStruct.h" + + +class OscReceiverInputKey : public IOscReceiverInterface +{ +public: + OscReceiverInputKey(const FString & address); + + OscReceiverInputKey(const OscReceiverInputKey &) = delete; + OscReceiverInputKey& operator=(const OscReceiverInputKey &) = delete; + +private: + + /// Adds the custom input key to the Slate application. + void RegisterKey() const; + + const FString & GetAddressFilter() const override + { + return _address; + } + + void SendEvent(const FName & Address, const TArray & Data) override; + +private: + FString _address; + FName _addressName; + FKey _key; +}; diff --git a/OSC/Source/OSC/Private/Receive/OscReceiverInterface.cpp b/OSC/Source/OSC/Private/Receive/OscReceiverInterface.cpp deleted file mode 100644 index a16a564..0000000 --- a/OSC/Source/OSC/Private/Receive/OscReceiverInterface.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "OscPrivatePCH.h" -#include "OscReceiverInterface.h" - - -UOscReceiverInterface::UOscReceiverInterface(const class FPostConstructInitializeProperties& PCIP) -: Super(PCIP) -{ - -} - -const FString & IOscReceiverInterface::GetAddressFilter() -{ - static FString defaultValue; - return defaultValue; -} - -void IOscReceiverInterface::SendEvent(const FName & Address, const TArray & Data) -{ } diff --git a/OSC/Source/OSC/Private/Receive/OscReceiverInterface.h b/OSC/Source/OSC/Private/Receive/OscReceiverInterface.h index 0bcc6e2..cdaaaf9 100644 --- a/OSC/Source/OSC/Private/Receive/OscReceiverInterface.h +++ b/OSC/Source/OSC/Private/Receive/OscReceiverInterface.h @@ -1,19 +1,33 @@ #pragma once #include "OscDataElemStruct.h" -#include "OscReceiverInterface.generated.h" -UINTERFACE(MinimalAPI) -class UOscReceiverInterface : public UInterface +/// Base class to receive OSC messages. +struct IOscReceiverInterface { - GENERATED_UINTERFACE_BODY() + virtual const FString & GetAddressFilter() const = 0; + virtual void SendEvent(const FName & Address, const TArray & Data) = 0; }; -class IOscReceiverInterface -{ - GENERATED_IINTERFACE_BODY() - virtual const FString & GetAddressFilter(); - virtual void SendEvent(const FName & Address, const TArray & Data); +/// Forward calls to an impl object. +template +struct BasicOscReceiver : IOscReceiverInterface +{ + T * const _impl; + + BasicOscReceiver(T * impl) : _impl(impl) + { + } + + const FString & GetAddressFilter() const final + { + return _impl->GetAddressFilter(); + } + + void SendEvent(const FName & Address, const TArray & Data) final + { + _impl->SendEvent(Address, Data); + } };