diff options
Diffstat (limited to 'wrapper')
| -rw-r--r-- | wrapper/input.cpp | 74 | ||||
| -rw-r--r-- | wrapper/interop-exception.h | 21 | ||||
| -rw-r--r-- | wrapper/wrapper.cpp | 414 | ||||
| -rw-r--r-- | wrapper/wrapper.vcxproj | 3 | ||||
| -rw-r--r-- | wrapper/wrapper.vcxproj.filters | 6 |
5 files changed, 375 insertions, 143 deletions
diff --git a/wrapper/input.cpp b/wrapper/input.cpp new file mode 100644 index 0000000..d50f774 --- /dev/null +++ b/wrapper/input.cpp @@ -0,0 +1,74 @@ +#include "interop-exception.h" + +#include <utility-rawinput.hpp> +#include <algorithm> +#include <msclr\marshal_cppstd.h> + +using namespace System; +using namespace System::Collections::Generic; + +struct device_info { + std::wstring name; + std::wstring id; +}; + +std::vector<device_info> get_unique_device_info() { + std::vector<device_info> info; + + rawinput_foreach_with_interface([&](const auto& dev, const WCHAR* name) { + info.push_back({ + L"", // get_property_wstr(name, &DEVPKEY_Device_FriendlyName), /* doesn't work */ + dev_id_from_interface(name) + }); + }); + + std::sort(info.begin(), info.end(), + [](auto&& l, auto&& r) { return l.id < r.id; }); + auto last = std::unique(info.begin(), info.end(), + [](auto&& l, auto&& r) { return l.id == r.id; }); + info.erase(last, info.end()); + + return info; +} + +public ref struct RawInputInterop +{ + static void AddHandlesFromID(String^ deviceID, List<IntPtr>^ rawInputHandles) + { + try + { + std::vector<HANDLE> nativeHandles = rawinput_handles_from_dev_id( + msclr::interop::marshal_as<std::wstring>(deviceID)); + + for (auto nh : nativeHandles) rawInputHandles->Add(IntPtr(nh)); + } + catch (const std::exception& e) + { + throw gcnew RawInputInteropException(e); + } + } + + static List<ValueTuple<String^, String^>>^ GetDeviceIDs() + { + try + { + auto managed = gcnew List<ValueTuple<String^, String^>>(); + + for (auto&& [name, id] : get_unique_device_info()) + { + managed->Add( + ValueTuple<String^, String^>( + msclr::interop::marshal_as<String^>(name), + msclr::interop::marshal_as<String^>(id))); + } + + return managed; + } + catch (const std::exception& e) + { + throw gcnew RawInputInteropException(e); + } + } + +}; + diff --git a/wrapper/interop-exception.h b/wrapper/interop-exception.h new file mode 100644 index 0000000..8ebae5c --- /dev/null +++ b/wrapper/interop-exception.h @@ -0,0 +1,21 @@ +#pragma once + +#include <exception> + +public ref struct InteropException : System::Exception { + InteropException(System::String^ what) : + Exception(what) {} + InteropException(const char* what) : + Exception(gcnew System::String(what)) {} + InteropException(const std::exception& e) : + InteropException(e.what()) {} +}; + +public ref struct RawInputInteropException : InteropException { + RawInputInteropException(System::String^ what) : + InteropException(what) {} + RawInputInteropException(const char* what) : + InteropException(what) {} + RawInputInteropException(const std::exception& e) : + InteropException(e) {} +}; diff --git a/wrapper/wrapper.cpp b/wrapper/wrapper.cpp index 7583312..eb7d4d0 100644 --- a/wrapper/wrapper.cpp +++ b/wrapper/wrapper.cpp @@ -1,22 +1,17 @@ #pragma once +#include "interop-exception.h" + #include <rawaccel-io.hpp> #include <rawaccel-validate.hpp> -#include <utility-rawinput.hpp> - -#include <algorithm> -#include <type_traits> -#include <msclr\marshal_cppstd.h> using namespace System; using namespace System::Collections::Generic; -using namespace System::IO; using namespace System::Runtime::InteropServices; using namespace System::Reflection; -using namespace Windows::Forms; - using namespace Newtonsoft::Json; +using namespace Newtonsoft::Json::Linq; namespace ra = rawaccel; @@ -25,17 +20,13 @@ ra::settings default_settings; [JsonConverter(Converters::StringEnumConverter::typeid)] public enum class AccelMode { - classic, jump, natural, power, motivity, lut, noaccel + classic, jump, natural, power, motivity, noaccel }; +[JsonConverter(Converters::StringEnumConverter::typeid)] public enum class TableMode { - off, binlog, linear -}; - -public enum class TableType -{ - spaced, arbitrary + off, binlog, linear, arbitrary }; [StructLayout(LayoutKind::Sequential)] @@ -44,9 +35,6 @@ public value struct TableArgs [JsonIgnore] TableMode mode; - [JsonIgnore] - TableType type; - [MarshalAs(UnmanagedType::U1)] bool transfer; @@ -67,24 +55,6 @@ public value struct Vec2 T y; }; -public ref struct SpacedTable -{ - [JsonProperty("Arguments for spacing in table")] - TableArgs args; - - [JsonProperty("Series of points for use in curve")] - List<double>^ points; -}; - -public ref struct ArbitraryTable -{ - [JsonProperty("Whether points affect velocity (true) or sensitivity (false)")] - bool transfer; - - [JsonProperty("Series of points for use in curve")] - List<Vec2<double>>^ points; -}; - [StructLayout(LayoutKind::Sequential)] public value struct AccelArgs { @@ -171,12 +141,6 @@ public ref struct DriverSettings [MarshalAs(UnmanagedType::ByValTStr, SizeConst = ra::MAX_DEV_ID_LEN)] String^ deviceID = ""; - [JsonIgnore] - SpacedTable^ SpacedTable; - - [JsonIgnore] - ArbitraryTable^ ArbitraryTable; - bool ShouldSerializeminimumTime() { return minimumTime != ra::DEFAULT_TIME_MIN; @@ -191,49 +155,254 @@ public ref struct DriverSettings { Marshal::PtrToStructure(IntPtr(&default_settings), this); } - - void ToFile(String^ path) +}; + +[JsonObject(ItemRequired = Required::Always)] +public ref struct LutBase abstract +{ + [JsonConverter(Converters::StringEnumConverter::typeid)] + enum class Mode { - using namespace Newtonsoft::Json::Linq; + logarithmic, linear, arbitrary + } mode; - JObject^ thisJO = JObject::FromObject(this); - String^ modes = String::Join(" | ", Enum::GetNames(AccelMode::typeid)); - thisJO->AddFirst(gcnew JProperty("### Accel Modes ###", modes)); - File::WriteAllText(path, thisJO->ToString(Formatting::Indented)); + virtual void SetArgs(TableArgs%) abstract; + virtual void SetData(ra::accel_union&) abstract; +}; + +[JsonObject(ItemRequired = Required::Always)] +public ref struct ArbitraryLut sealed : public LutBase +{ + [JsonProperty("Whether points affect velocity (true) or sensitivity (false)")] + bool transfer; + + array<float, 2>^ data; + + virtual void SetArgs(TableArgs% args) override + { + args.mode = TableMode::arbitrary; + args.transfer = transfer; } - static DriverSettings^ FromFile(String^ path) + virtual void SetData(ra::accel_union& accel) override { - if (!File::Exists(path)) - { - throw gcnew FileNotFoundException( - String::Format("Settings file not found at {0}", path)); + throw gcnew NotImplementedException(); + } + +}; + +[JsonObject(ItemRequired = Required::Always)] +public ref struct SpacedLut abstract : public LutBase +{ + [JsonProperty("Whether points affect velocity (true) or sensitivity (false)")] + bool transfer; + + double start; + double stop; + array<float>^ data; + + void SetArgsBase(TableArgs% args) + { + args.transfer = transfer; + args.start = start; + args.stop = stop; + } + + void SetDataBase(ra::accel_union& accel) + { + if (size_t(data->LongLength) > ra::LUT_CAPACITY) { + throw gcnew InteropException("data is too large"); } + } +}; - auto settings = JsonConvert::DeserializeObject<DriverSettings^>( - File::ReadAllText(path)); +[JsonObject(ItemRequired = Required::Always)] +public ref struct LinearLut sealed : public SpacedLut +{ + LinearLut(const ra::linear_lut& table) + { + mode = Mode::linear; + transfer = table.transfer; + start = table.range.start; + stop = table.range.stop; + data = gcnew array<float>(table.range.num); + + pin_ptr<float> pdata = &data[0]; + std::memcpy(pdata, &table.data, sizeof(float) * data->Length); + } - if (settings == nullptr) { - throw gcnew JsonException(String::Format("{0} contains invalid JSON", path)); + virtual void SetArgs(TableArgs% args) override + { + SetArgsBase(args); + args.num = data->Length; + args.mode = TableMode::linear; + } + + virtual void SetData(ra::accel_union& accel) override + { + SetDataBase(accel); + pin_ptr<float> pdata = &data[0]; + std::memcpy(&accel.lin_lut.data, pdata, sizeof(float) * data->Length); + } +}; + +[JsonObject(ItemRequired = Required::Always)] +public ref struct BinLogLut sealed : public SpacedLut +{ + short num; + + BinLogLut(const ra::binlog_lut& table) + { + mode = Mode::logarithmic; + transfer = table.transfer; + start = table.range.start; + stop = table.range.stop; + num = table.range.num; + data = gcnew array<float>(1 + num * (int(stop) - int(start))); + + pin_ptr<float> pdata = &data[0]; + std::memcpy(pdata, &table.data, sizeof(float) * data->Length); + } + + virtual void SetArgs(TableArgs% args) override + { + SetArgsBase(args); + args.num = num; + args.mode = TableMode::binlog; + } + + virtual void SetData(ra::accel_union& accel) override + { + SetDataBase(accel); + + if (data->Length != 1 + num * (int(stop) - int(start))) { + throw gcnew InteropException("size of data does not match args"); } - return settings; + pin_ptr<float> pdata = &data[0]; + std::memcpy(&accel.log_lut.data, pdata, sizeof(float) * data->Length); } }; -public ref struct InteropException : public Exception { -public: - InteropException(String^ what) : - Exception(what) {} - InteropException(const char* what) : - Exception(gcnew String(what)) {} - InteropException(const std::exception& e) : - InteropException(e.what()) {} +public ref struct RaConvert { + + static DriverSettings^ Settings(String^ json) + { + return NonNullable<DriverSettings^>(json); + } + + static String^ Settings(DriverSettings^ settings) + { + JObject^ jObject = JObject::FromObject(settings); + String^ modes = String::Join(" | ", Enum::GetNames(AccelMode::typeid)); + jObject->AddFirst(gcnew JProperty("### Accel Modes ###", modes)); + return jObject->ToString(Formatting::Indented); + } + + static LutBase^ Table(String^ json) + { + JObject^ jObject = JObject::Parse(json); + + if ((Object^)jObject == nullptr) { + throw gcnew JsonException("bad json"); + } + + LutBase^ base = NonNullable<LutBase^>(jObject); + + switch (base->mode) { + case LutBase::Mode::logarithmic: + return NonNullable<BinLogLut^>(jObject); + case LutBase::Mode::linear: + return NonNullable<LinearLut^>(jObject); + case LutBase::Mode::arbitrary: + return NonNullable<ArbitraryLut^>(jObject); + default: + throw gcnew NotImplementedException(); + } + } + + static String^ Table(LutBase^ lut) + { + auto serializerSettings = gcnew JsonSerializerSettings(); + return JsonConvert::SerializeObject( + lut, lut->GetType(), Formatting::Indented, serializerSettings); + }; + + generic <typename T> + static T NonNullable(String^ json) + { + T obj = JsonConvert::DeserializeObject<T>(json); + if (obj == nullptr) throw gcnew JsonException("invalid JSON"); + return obj; + } + + generic <typename T> + static T NonNullable(JObject^ jObject) + { + T obj = jObject->ToObject<T>(); + if (obj == nullptr) throw gcnew JsonException("invalid JSON"); + return obj; + } +}; + +public ref struct ExtendedSettings { + DriverSettings^ baseSettings; + Vec2<LutBase^> tables; + + using JSON = String^; + + ExtendedSettings(DriverSettings^ driverSettings) : + baseSettings(driverSettings) {} + + ExtendedSettings() : + ExtendedSettings(gcnew DriverSettings()) {} + + ExtendedSettings(JSON settingsJson) : + ExtendedSettings(settingsJson, nullptr, nullptr, false) {} + + ExtendedSettings(JSON settingsJson, JSON tableJson) : + ExtendedSettings(settingsJson, tableJson, nullptr, false) {} + + ExtendedSettings(JSON settingsJson, JSON xTableJson, JSON yTableJson) : + ExtendedSettings(settingsJson, xTableJson, yTableJson, true) {} + +private: + ExtendedSettings(JSON settingsJson, JSON xTableJson, JSON yTableJson, bool byComponent) + { + if (settingsJson) { + baseSettings = RaConvert::Settings(settingsJson); + } + else { + baseSettings = gcnew DriverSettings(); + } + + if (xTableJson || yTableJson) { + baseSettings->combineMagnitudes = !byComponent; + } + + if (xTableJson) { + tables.x = RaConvert::Table(xTableJson); + tables.x->SetArgs(baseSettings->args.x.lutArgs); + } + + if (yTableJson) { + if (Object::ReferenceEquals(yTableJson, xTableJson)) { + tables.y = tables.x; + } + else { + tables.y = RaConvert::Table(yTableJson); + } + + tables.y->SetArgs(baseSettings->args.y.lutArgs); + } + } + }; public ref class SettingsErrors { public: + List<String^>^ list; int countX; int countY; @@ -242,9 +411,12 @@ public: void Add(const char* msg) { - list->Add(msclr::interop::marshal_as<String^>(msg)); + list->Add(gcnew String(msg)); } + SettingsErrors(ExtendedSettings^ settings) : + SettingsErrors(settings->baseSettings) {} + SettingsErrors(DriverSettings^ settings) { MsgHandler^ del = gcnew MsgHandler(this, &SettingsErrors::Add); @@ -289,71 +461,6 @@ public: } }; -struct device_info { - std::wstring name; - std::wstring id; -}; - -std::vector<device_info> get_unique_device_info() { - std::vector<device_info> info; - - rawinput_foreach_with_interface([&](const auto& dev, const WCHAR* name) { - info.push_back({ - L"", // get_property_wstr(name, &DEVPKEY_Device_FriendlyName), /* doesn't work */ - dev_id_from_interface(name) - }); - }); - - std::sort(info.begin(), info.end(), - [](auto&& l, auto&& r) { return l.id < r.id; }); - auto last = std::unique(info.begin(), info.end(), - [](auto&& l, auto&& r) { return l.id == r.id; }); - info.erase(last, info.end()); - - return info; -} - -public ref struct RawInputInterop -{ - static void AddHandlesFromID(String^ deviceID, List<IntPtr>^ rawInputHandles) - { - try - { - std::vector<HANDLE> nativeHandles = rawinput_handles_from_dev_id( - msclr::interop::marshal_as<std::wstring>(deviceID)); - - for (auto nh : nativeHandles) rawInputHandles->Add(IntPtr(nh)); - } - catch (const std::exception& e) - { - throw gcnew InteropException(e); - } - } - - static List<ValueTuple<String^, String^>>^ GetDeviceIDs() - { - try - { - auto managed = gcnew List<ValueTuple<String^, String^>>(); - - for (auto&& [name, id] : get_unique_device_info()) - { - managed->Add( - ValueTuple<String^, String^>( - msclr::interop::marshal_as<String^>(name), - msclr::interop::marshal_as<String^>(id))); - } - - return managed; - } - catch (const std::exception& e) - { - throw gcnew InteropException(e); - } - } - -}; - struct instance_t { ra::io_t data; vec2<ra::accel_invoker> inv; @@ -364,9 +471,9 @@ public ref class ManagedAccel instance_t* const instance = new instance_t(); public: - ManagedAccel() {}; + ManagedAccel() {} - ManagedAccel(DriverSettings^ settings) + ManagedAccel(ExtendedSettings^ settings) { Settings = settings; } @@ -403,20 +510,32 @@ public: } } - property DriverSettings^ Settings + property ExtendedSettings^ Settings { - DriverSettings^ get() + ExtendedSettings^ get() { - DriverSettings^ settings = gcnew DriverSettings(); - Marshal::PtrToStructure(IntPtr(&instance->data.args), settings); + auto settings = gcnew ExtendedSettings(); + Marshal::PtrToStructure(IntPtr(&instance->data.args), settings->baseSettings); + settings->tables.x = extract(instance->data.args.argsv.x.lut_args.mode, + instance->data.mod.accels.x); + settings->tables.y = extract(instance->data.args.argsv.y.lut_args.mode, + instance->data.mod.accels.y); return settings; } - void set(DriverSettings^ val) + void set(ExtendedSettings^ val) { - Marshal::StructureToPtr(val, IntPtr(&instance->data.args), false); + Marshal::StructureToPtr(val->baseSettings, IntPtr(&instance->data.args), false); instance->data.mod = { instance->data.args }; instance->inv = ra::invokers(instance->data.args); + + if (val->tables.x) { + val->tables.x->SetData(instance->data.mod.accels.x); + } + + if (val->tables.y) { + val->tables.y->SetData(instance->data.mod.accels.y); + } } } @@ -433,6 +552,18 @@ public: throw gcnew InteropException(e); } } + +private: + LutBase^ extract(ra::table_mode mode, ra::accel_union& au) + { + switch (mode) { + case ra::table_mode::off: return nullptr; + case ra::table_mode::linear: return gcnew LinearLut(au.lin_lut); + case ra::table_mode::binlog: return gcnew BinLogLut(au.log_lut); + case ra::table_mode::arbitrary: + default: throw gcnew NotImplementedException(); + } + } }; public ref struct VersionHelper @@ -449,5 +580,4 @@ public ref struct VersionHelper throw gcnew InteropException(e); } } - }; diff --git a/wrapper/wrapper.vcxproj b/wrapper/wrapper.vcxproj index fba03a7..8de0cfb 100644 --- a/wrapper/wrapper.vcxproj +++ b/wrapper/wrapper.vcxproj @@ -83,17 +83,18 @@ copy /Y "$(TargetDir)Newtonsoft.Json.dll" "$(SolutionDir)signed\Newtonsoft.Json. </ResourceCompile> </ItemDefinitionGroup> <ItemGroup> + <ClInclude Include="interop-exception.h" /> <ClInclude Include="resource.h" /> </ItemGroup> <ItemGroup> <ClCompile Include="AssemblyInfo.cpp" /> + <ClCompile Include="input.cpp" /> <ClCompile Include="wrapper.cpp" /> </ItemGroup> <ItemGroup> <Reference Include="Newtonsoft.Json"> <HintPath>..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath> </Reference> - <Reference Include="System.Windows.Forms" /> </ItemGroup> <ItemGroup> <ResourceCompile Include="wrapper.rc" /> diff --git a/wrapper/wrapper.vcxproj.filters b/wrapper/wrapper.vcxproj.filters index 96b8e1a..9caeae1 100644 --- a/wrapper/wrapper.vcxproj.filters +++ b/wrapper/wrapper.vcxproj.filters @@ -18,6 +18,9 @@ <ClInclude Include="resource.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="interop-exception.h"> + <Filter>Header Files</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="wrapper.cpp"> @@ -26,6 +29,9 @@ <ClCompile Include="AssemblyInfo.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="input.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ResourceCompile Include="wrapper.rc"> |