From f7baa02190f185ec7ab85ce7b5777ddce3e0cf9c Mon Sep 17 00:00:00 2001
From: acdemiralp <demiralpali@gmail.com>
Date: Thu, 22 Nov 2018 02:51:55 +0100
Subject: [PATCH] Added initial version.

---
 .gitignore                                    |  74 ++++++++++++
 Resources/Icon128.png                         | Bin 0 -> 3727 bytes
 .../DisplayClusterInput.Build.cs              |  53 +++++++++
 .../Private/DisplayClusterInputDevice.cpp     | 110 ++++++++++++++++++
 .../Private/DisplayClusterInputDevice.h       |  24 ++++
 .../Private/DisplayClusterInputModule.cpp     |  22 ++++
 .../Private/DisplayClusterInputModule.h       |  12 ++
 .../Private/DisplayClusterKeys.cpp            |  15 +++
 .../Private/DisplayClusterKeys.h              |  20 ++++
 .../Public/IDisplayClusterInputModule.h       |  18 +++
 nDisplayInput.uplugin                         |  30 +++++
 11 files changed, 378 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 Resources/Icon128.png
 create mode 100644 Source/DisplayClusterInput/DisplayClusterInput.Build.cs
 create mode 100644 Source/DisplayClusterInput/Private/DisplayClusterInputDevice.cpp
 create mode 100644 Source/DisplayClusterInput/Private/DisplayClusterInputDevice.h
 create mode 100644 Source/DisplayClusterInput/Private/DisplayClusterInputModule.cpp
 create mode 100644 Source/DisplayClusterInput/Private/DisplayClusterInputModule.h
 create mode 100644 Source/DisplayClusterInput/Private/DisplayClusterKeys.cpp
 create mode 100644 Source/DisplayClusterInput/Private/DisplayClusterKeys.h
 create mode 100644 Source/DisplayClusterInput/Public/IDisplayClusterInputModule.h
 create mode 100644 nDisplayInput.uplugin

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..56a33f0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,74 @@
+# Visual Studio 2015 user specific files
+.vs/
+
+# Compiled Object files
+*.slo
+*.lo
+*.o
+*.obj
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+*.dll
+
+# Fortran module files
+*.mod
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.lib
+
+# Executables
+*.exe
+*.out
+*.app
+*.ipa
+
+# These project files can be generated by the engine
+*.xcodeproj
+*.xcworkspace
+*.sln
+*.suo
+*.opensdf
+*.sdf
+*.VC.db
+*.VC.opendb
+
+# Precompiled Assets
+SourceArt/**/*.png
+SourceArt/**/*.tga
+
+# Binary Files
+Binaries/*
+Plugins/*/Binaries/*
+
+# Builds
+Build/*
+
+# Whitelist PakBlacklist-<BuildConfiguration>.txt files
+!Build/*/
+Build/*/**
+!Build/*/PakBlacklist*.txt
+
+# Don't ignore icon files in Build
+!Build/**/*.ico
+
+# Built data for maps
+*_BuiltData.uasset
+
+# Configuration files generated by the Editor
+Saved/*
+
+# Compiled source files for the engine to use
+Intermediate/*
+Plugins/*/Intermediate/*
+
+# Cache files for the editor to use
+DerivedDataCache/*
\ No newline at end of file
diff --git a/Resources/Icon128.png b/Resources/Icon128.png
new file mode 100644
index 0000000000000000000000000000000000000000..26a268dbf061b39e58e9c8d86337d05d64a7b92e
GIT binary patch
literal 3727
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
B4m<z=

literal 0
HcmV?d00001

diff --git a/Source/DisplayClusterInput/DisplayClusterInput.Build.cs b/Source/DisplayClusterInput/DisplayClusterInput.Build.cs
new file mode 100644
index 0000000..94937c4
--- /dev/null
+++ b/Source/DisplayClusterInput/DisplayClusterInput.Build.cs
@@ -0,0 +1,53 @@
+using UnrealBuildTool;
+
+public class DisplayClusterInput : ModuleRules
+{
+  public DisplayClusterInput(ReadOnlyTargetRules Target) : base(Target)
+  {
+    PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
+
+    PublicIncludePaths.AddRange(
+      new string[]
+      {
+
+      }
+      );
+
+
+    PrivateIncludePaths.AddRange(
+      new string[]
+      {
+
+      }
+      );
+
+
+    PublicDependencyModuleNames.AddRange(
+      new string[]
+      {
+        "Core",
+        "CoreUObject",
+        "DisplayCluster",
+        "Engine",
+        "InputCore",
+        "InputDevice"
+      }
+      );
+
+
+    PrivateDependencyModuleNames.AddRange(
+      new string[]
+      {
+
+      }
+      );
+
+
+    DynamicallyLoadedModuleNames.AddRange(
+      new string[]
+      {
+
+      }
+      );
+  }
+}
diff --git a/Source/DisplayClusterInput/Private/DisplayClusterInputDevice.cpp b/Source/DisplayClusterInput/Private/DisplayClusterInputDevice.cpp
new file mode 100644
index 0000000..9eff51a
--- /dev/null
+++ b/Source/DisplayClusterInput/Private/DisplayClusterInputDevice.cpp
@@ -0,0 +1,110 @@
+#include "DisplayClusterInputDevice.h"
+
+#include "CoreMinimal.h"
+#include "Features/IModularFeatures.h"
+#include "Input/IDisplayClusterInputManager.h"
+#include "IDisplayCluster.h"
+
+#include "DisplayClusterKeys.h"
+
+#define LOCTEXT_NAMESPACE "DisplayClusterInput"
+
+FDisplayClusterInputDevice::FDisplayClusterInputDevice(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler) : MessageHandler(InMessageHandler)
+{
+  AxisIndices   = TArray<const FKey*>
+  {
+    &FDisplayClusterKeys::AxisX,
+    &FDisplayClusterKeys::AxisY
+  };
+  ButtonIndices = TArray<const FKey*> 
+  { 
+    &FDisplayClusterKeys::Trigger, 
+    &FDisplayClusterKeys::Action1,
+    &FDisplayClusterKeys::Action2,
+    &FDisplayClusterKeys::Action3,
+    &FDisplayClusterKeys::Action4,
+    &FDisplayClusterKeys::Action5,
+    &FDisplayClusterKeys::Action6,
+    &FDisplayClusterKeys::Action7,
+    &FDisplayClusterKeys::Action8,
+    &FDisplayClusterKeys::Action9
+  };
+}
+FDisplayClusterInputDevice::~FDisplayClusterInputDevice()
+{
+
+}
+
+void FDisplayClusterInputDevice::PreInit             ()
+{
+  EKeys::AddMenuCategoryDisplayInfo("DisplayCluster", LOCTEXT("DisplayClusterSubCategory", "Display Cluster"), TEXT("GraphEditor.PadEvent_16x"));
+
+  EKeys::AddKey(FKeyDetails(FDisplayClusterKeys::Trigger, LOCTEXT("DisplayClusterTrigger", "Display Cluster Trigger" ), FKeyDetails::GamepadKey, "DisplayCluster"));
+  EKeys::AddKey(FKeyDetails(FDisplayClusterKeys::Action1, LOCTEXT("DisplayClusterAction1", "Display Cluster Action 1"), FKeyDetails::GamepadKey, "DisplayCluster"));
+  EKeys::AddKey(FKeyDetails(FDisplayClusterKeys::Action2, LOCTEXT("DisplayClusterAction2", "Display Cluster Action 2"), FKeyDetails::GamepadKey, "DisplayCluster"));
+  EKeys::AddKey(FKeyDetails(FDisplayClusterKeys::Action3, LOCTEXT("DisplayClusterAction3", "Display Cluster Action 3"), FKeyDetails::GamepadKey, "DisplayCluster"));
+  EKeys::AddKey(FKeyDetails(FDisplayClusterKeys::Action4, LOCTEXT("DisplayClusterAction4", "Display Cluster Action 4"), FKeyDetails::GamepadKey, "DisplayCluster"));
+  EKeys::AddKey(FKeyDetails(FDisplayClusterKeys::Action5, LOCTEXT("DisplayClusterAction5", "Display Cluster Action 5"), FKeyDetails::GamepadKey, "DisplayCluster"));
+  EKeys::AddKey(FKeyDetails(FDisplayClusterKeys::Action6, LOCTEXT("DisplayClusterAction6", "Display Cluster Action 6"), FKeyDetails::GamepadKey, "DisplayCluster"));
+  EKeys::AddKey(FKeyDetails(FDisplayClusterKeys::Action7, LOCTEXT("DisplayClusterAction7", "Display Cluster Action 7"), FKeyDetails::GamepadKey, "DisplayCluster"));
+  EKeys::AddKey(FKeyDetails(FDisplayClusterKeys::Action8, LOCTEXT("DisplayClusterAction8", "Display Cluster Action 8"), FKeyDetails::GamepadKey, "DisplayCluster"));
+  EKeys::AddKey(FKeyDetails(FDisplayClusterKeys::Action9, LOCTEXT("DisplayClusterAction9", "Display Cluster Action 9"), FKeyDetails::GamepadKey, "DisplayCluster"));
+
+  EKeys::AddKey(FKeyDetails(FDisplayClusterKeys::AxisX  , LOCTEXT("DisplayClusterAxisX"  , "Display Cluster X Axis"  ), FKeyDetails::FloatAxis , "DisplayCluster"));
+  EKeys::AddKey(FKeyDetails(FDisplayClusterKeys::AxisY  , LOCTEXT("DisplayClusterAxisY"  , "Display Cluster Y Axis"  ), FKeyDetails::FloatAxis , "DisplayCluster"));
+}
+
+bool FDisplayClusterInputDevice::Exec                (UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar)
+{
+  return false;
+}                 
+void FDisplayClusterInputDevice::SendControllerEvents()
+{
+  if (!IDisplayCluster::IsAvailable()) return;
+
+  auto InputManager = IDisplayCluster::Get().GetInputMgr();
+  
+  TArray<FString> AxisDeviceIds;
+  InputManager->GetAxisDeviceIds(AxisDeviceIds);
+  for (auto i = 0; i < AxisDeviceIds.Num(); ++i)
+  {
+    for (auto j = 0; j < AxisIndices.Num(); ++j)
+    {
+      float Value;
+      InputManager->GetAxis(AxisDeviceIds[i], j, Value);
+      if (Value != 0.0f) MessageHandler->OnControllerAnalog(AxisIndices[j]->GetFName(), i, Value);
+    }
+  }
+
+  TArray<FString> ButtonDeviceIds;
+  InputManager->GetButtonDeviceIds(ButtonDeviceIds);
+  for (auto i = 0; i < ButtonDeviceIds.Num(); ++i)
+  {
+    for (auto j = 0; j < ButtonIndices.Num(); ++j)
+    {
+      bool Pressed, Released;
+      InputManager->WasButtonPressed (ButtonDeviceIds[i], j, Pressed );
+      InputManager->WasButtonReleased(ButtonDeviceIds[i], j, Released);
+      if (Pressed ) MessageHandler->OnControllerButtonPressed (ButtonIndices[j]->GetFName(), i, false);
+      if (Released) MessageHandler->OnControllerButtonReleased(ButtonIndices[j]->GetFName(), i, false);
+    }
+  }
+}                 
+void FDisplayClusterInputDevice::SetChannelValue     (int32 ControllerId, FForceFeedbackChannelType ChannelType, float Value)
+{
+  // Intentionally empty (no haptic support).
+} 
+void FDisplayClusterInputDevice::SetChannelValues    (int32 ControllerId, const FForceFeedbackValues& Values)
+{
+  // Intentionally empty (no haptic support).
+}
+void FDisplayClusterInputDevice::SetMessageHandler   (const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler)
+{
+  MessageHandler = InMessageHandler;
+}
+void FDisplayClusterInputDevice::Tick                (float DeltaTime)
+{
+  // Intentionally empty (everything is handled in SendControllerEvents above).
+}
+
+#undef LOCTEXT_NAMESPACE
\ No newline at end of file
diff --git a/Source/DisplayClusterInput/Private/DisplayClusterInputDevice.h b/Source/DisplayClusterInput/Private/DisplayClusterInputDevice.h
new file mode 100644
index 0000000..c04c6f4
--- /dev/null
+++ b/Source/DisplayClusterInput/Private/DisplayClusterInputDevice.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "IInputDevice.h"
+
+class FDisplayClusterInputDevice : public IInputDevice
+{
+public:
+  FDisplayClusterInputDevice(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler);
+  virtual ~FDisplayClusterInputDevice();
+
+  static  void PreInit             ();
+
+	virtual bool Exec                (UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar)                   override;
+	virtual void SendControllerEvents()                                                                       override;
+	virtual void SetChannelValue     (int32 ControllerId, FForceFeedbackChannelType ChannelType, float Value) override;
+	virtual void SetChannelValues    (int32 ControllerId, const FForceFeedbackValues& Values)                 override;
+	virtual void SetMessageHandler   (const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler)  override;
+	virtual void Tick                (float DeltaTime)                                                        override;
+
+private:
+  TSharedPtr<FGenericApplicationMessageHandler> MessageHandler;
+  TArray<const FKey*>                           AxisIndices   ;
+  TArray<const FKey*>                           ButtonIndices ;
+};
\ No newline at end of file
diff --git a/Source/DisplayClusterInput/Private/DisplayClusterInputModule.cpp b/Source/DisplayClusterInput/Private/DisplayClusterInputModule.cpp
new file mode 100644
index 0000000..8f69d94
--- /dev/null
+++ b/Source/DisplayClusterInput/Private/DisplayClusterInputModule.cpp
@@ -0,0 +1,22 @@
+#include "DisplayClusterInputModule.h"
+
+#include "IDisplayCluster.h"
+
+#include "DisplayClusterInputDevice.h"
+
+#define LOCTEXT_NAMESPACE "DisplayClusterInput"
+
+void FDisplayClusterInputModule::StartupModule()
+{
+  IInputDeviceModule::StartupModule();
+  FDisplayClusterInputDevice::PreInit();
+}
+
+TSharedPtr<class IInputDevice> FDisplayClusterInputModule::CreateInputDevice(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler)
+{
+  return TSharedPtr<FDisplayClusterInputDevice>(new FDisplayClusterInputDevice(InMessageHandler));
+}
+
+#undef LOCTEXT_NAMESPACE
+
+IMPLEMENT_MODULE(FDisplayClusterInputModule, DisplayClusterInput)
diff --git a/Source/DisplayClusterInput/Private/DisplayClusterInputModule.h b/Source/DisplayClusterInput/Private/DisplayClusterInputModule.h
new file mode 100644
index 0000000..f78b17b
--- /dev/null
+++ b/Source/DisplayClusterInput/Private/DisplayClusterInputModule.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "Templates/SharedPointer.h"
+#include "IInputDevice.h"
+
+#include "IDisplayClusterInputModule.h"
+
+class FDisplayClusterInputModule : public IDisplayClusterInputModule
+{
+  virtual void                           StartupModule    ()                                                                      override;
+  virtual TSharedPtr<class IInputDevice> CreateInputDevice(const TSharedRef<FGenericApplicationMessageHandler>& InMessageHandler) override;
+};
\ No newline at end of file
diff --git a/Source/DisplayClusterInput/Private/DisplayClusterKeys.cpp b/Source/DisplayClusterInput/Private/DisplayClusterKeys.cpp
new file mode 100644
index 0000000..8a6da50
--- /dev/null
+++ b/Source/DisplayClusterInput/Private/DisplayClusterKeys.cpp
@@ -0,0 +1,15 @@
+#include "DisplayClusterKeys.h"
+
+const FKey FDisplayClusterKeys::Trigger("DisplayClusterTrigger");
+const FKey FDisplayClusterKeys::Action1("DisplayClusterAction1");
+const FKey FDisplayClusterKeys::Action2("DisplayClusterAction2");
+const FKey FDisplayClusterKeys::Action3("DisplayClusterAction3");
+const FKey FDisplayClusterKeys::Action4("DisplayClusterAction4");
+const FKey FDisplayClusterKeys::Action5("DisplayClusterAction5");
+const FKey FDisplayClusterKeys::Action6("DisplayClusterAction6");
+const FKey FDisplayClusterKeys::Action7("DisplayClusterAction7");
+const FKey FDisplayClusterKeys::Action8("DisplayClusterAction8");
+const FKey FDisplayClusterKeys::Action9("DisplayClusterAction9");
+
+const FKey FDisplayClusterKeys::AxisX  ("DisplayClusterAxisX"  );
+const FKey FDisplayClusterKeys::AxisY  ("DisplayClusterAxisY"  );
diff --git a/Source/DisplayClusterInput/Private/DisplayClusterKeys.h b/Source/DisplayClusterInput/Private/DisplayClusterKeys.h
new file mode 100644
index 0000000..f91cc6d
--- /dev/null
+++ b/Source/DisplayClusterInput/Private/DisplayClusterKeys.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "InputCoreTypes.h"
+
+struct FDisplayClusterKeys
+{
+  static const FKey Trigger;
+  static const FKey Action1;
+  static const FKey Action2;
+  static const FKey Action3;
+  static const FKey Action4;
+  static const FKey Action5;
+  static const FKey Action6;
+  static const FKey Action7;
+  static const FKey Action8;
+  static const FKey Action9;
+
+  static const FKey AxisX  ;
+  static const FKey AxisY  ;
+};
\ No newline at end of file
diff --git a/Source/DisplayClusterInput/Public/IDisplayClusterInputModule.h b/Source/DisplayClusterInput/Public/IDisplayClusterInputModule.h
new file mode 100644
index 0000000..c1bcff4
--- /dev/null
+++ b/Source/DisplayClusterInput/Public/IDisplayClusterInputModule.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "Modules/ModuleManager.h"
+#include "IInputDeviceModule.h"
+
+class IDisplayClusterInputModule : public IInputDeviceModule
+{
+public:
+	static inline IDisplayClusterInputModule& Get()
+	{
+		return FModuleManager::LoadModuleChecked<IDisplayClusterInputModule>("DisplayClusterInput");
+	}
+
+	static inline bool IsAvailable()
+	{
+		return FModuleManager::Get().IsModuleLoaded("DisplayClusterInput");
+	}
+};
diff --git a/nDisplayInput.uplugin b/nDisplayInput.uplugin
new file mode 100644
index 0000000..cb78bd2
--- /dev/null
+++ b/nDisplayInput.uplugin
@@ -0,0 +1,30 @@
+{
+  "FileVersion": 3,
+  "Version": 1,
+  "VersionName": "1.0",
+  "FriendlyName": "nDisplayInput",
+  "Description": "",
+  "Category": "Other",
+  "CreatedBy": "",
+  "CreatedByURL": "",
+  "DocsURL": "",
+  "MarketplaceURL": "",
+  "SupportURL": "",
+  "CanContainContent": true,
+  "IsBetaVersion": false,
+  "Installed": false,
+  "EnabledByDefault": true,
+  "Modules": [
+    {
+      "Name": "DisplayClusterInput",
+      "Type": "Developer",
+      "LoadingPhase": "Default"
+    }
+  ],
+  "Plugins": [
+    {
+      "Name": "nDisplay",
+      "Enabled": true
+    }
+  ]
+}
\ No newline at end of file
-- 
GitLab