diff options
| author | a1xd <[email protected]> | 2021-09-09 17:28:10 -0400 |
|---|---|---|
| committer | a1xd <[email protected]> | 2021-09-09 17:28:10 -0400 |
| commit | 1964548acbc56edb346d2e396eb0108010d869e1 (patch) | |
| tree | 1f709352d4ff564df385afd3cb8a341d52134644 /wrapper/wrapper.cpp | |
| parent | rename driver_settings (diff) | |
| download | rawaccel-1964548acbc56edb346d2e396eb0108010d869e1.tar.xz rawaccel-1964548acbc56edb346d2e396eb0108010d869e1.zip | |
port to .NET 5
dependency changes
- Newtonsoft.JSON
+ System.Windows.Forms.DataVisualization
+ System.Data.SqlClient (indirect, but was not added automatically by NuGet)
added ARM64 target
Diffstat (limited to 'wrapper/wrapper.cpp')
| -rw-r--r-- | wrapper/wrapper.cpp | 356 |
1 files changed, 238 insertions, 118 deletions
diff --git a/wrapper/wrapper.cpp b/wrapper/wrapper.cpp index c0b3b22..9a7b5f7 100644 --- a/wrapper/wrapper.cpp +++ b/wrapper/wrapper.cpp @@ -10,9 +10,8 @@ using namespace System::Collections::Generic; using namespace System::Runtime::InteropServices; using namespace System::Reflection; using namespace System::Runtime::Serialization; -using namespace Newtonsoft::Json; -using namespace Newtonsoft::Json::Linq; - +using namespace System::Text::Json; +using namespace System::Text::Json::Serialization; namespace ra = rawaccel; ra::modifier_settings default_modifier_settings; @@ -34,13 +33,13 @@ public ref struct VersionHelper } }; -[JsonConverter(Converters::StringEnumConverter::typeid)] +[JsonConverter(JsonStringEnumConverter::typeid)] public enum class AccelMode { classic, jump, natural, motivity, power, lut, noaccel }; -[JsonConverter(Converters::StringEnumConverter::typeid)] +[JsonConverter(JsonStringEnumConverter::typeid)] public enum class ClassicCapMode { in_out, input, output }; @@ -49,7 +48,10 @@ generic <typename T> [StructLayout(LayoutKind::Sequential)] public value struct Vec2 { + [JsonInclude] T x; + + [JsonInclude] T y; }; @@ -58,87 +60,201 @@ public value struct AccelArgs { literal int MaxLutPoints = ra::LUT_POINTS_CAPACITY; + [JsonInclude] AccelMode mode; - [JsonProperty("Gain / Velocity")] + [JsonPropertyName("Gain / Velocity")] [MarshalAs(UnmanagedType::U1)] + [JsonInclude] bool gain; + [JsonInclude] double offset; + + [JsonInclude] double acceleration; + + [JsonInclude] double decayRate; + + [JsonInclude] double growthRate; + + [JsonInclude] double motivity; + + [JsonInclude] double exponentClassic; + + [JsonInclude] double scale; + + [JsonInclude] double weight; + + [JsonInclude] double exponentPower; + + [JsonInclude] double limit; + + [JsonInclude] double midpoint; + + [JsonInclude] double smooth; - [JsonProperty("Cap / Jump")] + [JsonInclude] + [JsonPropertyName("Cap / Jump")] Vec2<double> cap; - [JsonProperty("Cap mode")] + [JsonInclude] + [JsonPropertyName("Cap mode")] ClassicCapMode capMode; [JsonIgnore] int length; + [JsonInclude] [MarshalAs(UnmanagedType::ByValArray, SizeConst = ra::LUT_RAW_DATA_CAPACITY)] array<float>^ data; +}; + +public ref struct AccelArgsConverter : JsonConverter<AccelArgs> +{ + virtual void Write(Utf8JsonWriter^ writer, AccelArgs args, JsonSerializerOptions^ options) override + { + if (args.mode == AccelMode::lut) { + // data->Length must be fixed for interop, + // temporary resize avoids serializing a bunch of zeros + Array::Resize(args.data, args.length); + JsonSerializer::Serialize(writer, args, options); + Array::Resize(args.data, ra::LUT_RAW_DATA_CAPACITY); + } + else { + // data may be used internally in any mode, + // so hide it when it's not needed for deserialization + auto tmp = args.data; + args.data = Array::Empty<float>(); + JsonSerializer::Serialize(writer, args, options); + args.data = tmp; + } + } + + virtual AccelArgs Read(Utf8JsonReader% reader, Type^ type, JsonSerializerOptions^ options) override + { + AccelArgs args = JsonSerializer::Deserialize<AccelArgs>(reader, options); + + if (args.data == nullptr) args.data = Array::Empty<float>(); + + args.length = args.data->Length; + array<float>::Resize(args.data, ra::LUT_RAW_DATA_CAPACITY); + + return args; + } +}; + +public ref struct NullToEmptyStringConverter : JsonConverter<String^> +{ + virtual String^ Read(Utf8JsonReader% reader, Type^ type, JsonSerializerOptions^ options) override + { + String^ s = JsonSerializer::Deserialize<String^>(reader, options); + return s ? s : String::Empty; + } + + virtual void Write(Utf8JsonWriter^ writer, String^ s, JsonSerializerOptions^ options) override + { + JsonSerializer::Serialize(writer, s, options); + } - [OnDeserialized] - void OnDeserializedMethod(StreamingContext context) + property bool HandleNull { - // data->Length must match SizeConst when marshalling - length = data->Length; - array<float>::Resize(data, ra::LUT_RAW_DATA_CAPACITY); + virtual bool get() override { return true; } } }; -[JsonObject(ItemRequired = Required::Always)] +template <typename T> +public ref struct NullToDefaultRefClassConverter : JsonConverter<T^> +{ + virtual T^ Read(Utf8JsonReader% reader, Type^ type, JsonSerializerOptions^ options) override + { + T^ val = JsonSerializer::Deserialize<T^>(reader, options); + return val ? val : gcnew T(); + } + + virtual void Write(Utf8JsonWriter^ writer, T^ val, JsonSerializerOptions^ options) override + { + JsonSerializer::Serialize(writer, val, options); + } + + property bool HandleNull + { + virtual bool get() override { return true; } + } +}; + +template <typename T> +using NullToEmptyListConverter = NullToDefaultRefClassConverter<List<T>>; + [StructLayout(LayoutKind::Sequential, CharSet = CharSet::Unicode)] public ref struct Profile { + [JsonInclude] + [JsonConverter(NullToEmptyStringConverter::typeid)] [MarshalAs(UnmanagedType::ByValTStr, SizeConst = ra::MAX_NAME_LEN)] System::String^ name; - [JsonProperty("Whole/combined accel (set false for 'by component' mode)")] + [JsonInclude] + [JsonPropertyName("Whole/combined accel (set false for By-component mode)")] [MarshalAs(UnmanagedType::U1)] bool combineMagnitudes; + [JsonInclude] double lpNorm; - [JsonProperty("Stretches domain for horizontal vs vertical inputs")] + [JsonInclude] + [JsonPropertyName("Stretches domain for horizontal vs vertical inputs")] Vec2<double> domainXY; - [JsonProperty("Stretches accel range for horizontal vs vertical inputs")] + + [JsonInclude] + [JsonPropertyName("Stretches accel range for horizontal vs vertical inputs")] Vec2<double> rangeXY; - [JsonProperty("Sensitivity multiplier")] + [JsonInclude] + [JsonPropertyName("Sensitivity multiplier")] double sensitivity; - [JsonProperty("Y/X sensitivity ratio (vertical sens multiplier)")] + [JsonInclude] + [JsonPropertyName("Y/X sensitivity ratio (vertical sens multiplier)")] double yxSensRatio; - [JsonProperty("Whole or horizontal accel parameters")] + [JsonConverter(AccelArgsConverter::typeid)] + [JsonInclude] + [JsonPropertyName("Whole or horizontal accel parameters")] AccelArgs argsX; - [JsonProperty("Vertical accel parameters")] + + [JsonConverter(AccelArgsConverter::typeid)] + [JsonInclude] + [JsonPropertyName("Vertical accel parameters")] AccelArgs argsY; [JsonIgnore] double minimumSpeed; - [JsonProperty("Input Speed Cap")] + + [JsonInclude] + [JsonPropertyName("Input Speed Cap")] double maximumSpeed; - [JsonProperty("Negative directional multipliers")] + [JsonInclude] + [JsonPropertyName("Negative directional multipliers")] Vec2<double> directionalMultipliers; - [JsonProperty("Degrees of rotation")] + [JsonInclude] + [JsonPropertyName("Degrees of rotation")] double rotation; - [JsonProperty("Degrees of angle snapping")] + [JsonInclude] + [JsonPropertyName("Degrees of angle snapping")] double snap; Profile(ra::profile& args) @@ -150,45 +266,36 @@ public ref struct Profile Profile(default_modifier_settings.prof) {} }; -[JsonObject(ItemRequired = Required::Always)] [StructLayout(LayoutKind::Sequential)] public value struct DeviceConfig { + literal String^ dpiProperty = + "DPI (normalizes sens to 1000dpi and converts input speed unit (counts/ms to in/s)"; + literal String^ pollingRateProperty = + "Polling rate Hz (keep at 0 for automatic adjustment)"; + + [JsonInclude] [MarshalAs(UnmanagedType::U1)] bool disable; + [MarshalAs(UnmanagedType::U1)] - [JsonProperty(Required = Required::Default)] + [JsonInclude] bool setExtraInfo; - [JsonProperty("DPI (normalizes sens to 1000dpi and converts input speed unit: counts/ms -> in/s)")] + [JsonInclude] + [JsonPropertyName(dpiProperty)] int dpi; - [JsonProperty("Polling rate Hz (keep at 0 for automatic adjustment)")] + [JsonInclude] + [JsonPropertyName(pollingRateProperty)] int pollingRate; - - [ComponentModel::DefaultValue(ra::DEFAULT_TIME_MIN)] - [JsonProperty(Required = Required::Default)] + + [JsonInclude] double minimumTime; - [ComponentModel::DefaultValue(ra::DEFAULT_TIME_MAX)] - [JsonProperty(Required = Required::Default)] + [JsonInclude] double maximumTime; - bool ShouldSerializesetExtraInfo() - { - return setExtraInfo == true; - } - - bool ShouldSerializeminimumTime() - { - return minimumTime != ra::DEFAULT_TIME_MIN; - } - - bool ShouldSerializemaximumTime() - { - return maximumTime != ra::DEFAULT_TIME_MAX; - } - void Init(const ra::device_config& cfg) { disable = cfg.disable; @@ -200,19 +307,64 @@ public value struct DeviceConfig { } }; -[JsonObject(ItemRequired = Required::Always)] +public ref struct DeviceConfigConverter : JsonConverter<DeviceConfig> +{ + virtual DeviceConfig Read(Utf8JsonReader% reader, Type^ type, JsonSerializerOptions^ options) override + { + DeviceConfig cfg = JsonSerializer::Deserialize<DeviceConfig>(reader, options); + + if (cfg.maximumTime == 0) cfg.maximumTime = ra::DEFAULT_TIME_MAX; + if (cfg.minimumTime == 0) cfg.minimumTime = ra::DEFAULT_TIME_MIN; + + return cfg; + } + + virtual void Write(Utf8JsonWriter^ writer, DeviceConfig cfg, JsonSerializerOptions^ options) override + { + writer->WriteStartObject(); + + writer->WriteBoolean("disable", cfg.disable); + + if (cfg.setExtraInfo) { + writer->WriteBoolean("setExtraInfo", cfg.setExtraInfo); + } + + writer->WriteNumber(DeviceConfig::dpiProperty, cfg.dpi); + writer->WriteNumber(DeviceConfig::pollingRateProperty, cfg.pollingRate); + + if (cfg.minimumTime != ra::DEFAULT_TIME_MIN) { + writer->WriteNumber("minimumTime", cfg.minimumTime); + } + + if (cfg.maximumTime != ra::DEFAULT_TIME_MAX) { + writer->WriteNumber("maximumTime", cfg.maximumTime); + } + + writer->WriteEndObject(); + } +}; + + [StructLayout(LayoutKind::Sequential, CharSet = CharSet::Unicode)] public ref struct DeviceSettings { + [JsonInclude] + [JsonConverter(NullToEmptyStringConverter::typeid)] [MarshalAs(UnmanagedType::ByValTStr, SizeConst = ra::MAX_NAME_LEN)] String^ name; + [JsonInclude] + [JsonConverter(NullToEmptyStringConverter::typeid)] [MarshalAs(UnmanagedType::ByValTStr, SizeConst = ra::MAX_NAME_LEN)] String^ profile; + [JsonInclude] + [JsonConverter(NullToEmptyStringConverter::typeid)] [MarshalAs(UnmanagedType::ByValTStr, SizeConst = ra::MAX_DEV_ID_LEN)] String^ id; + [JsonInclude] + [JsonConverter(DeviceConfigConverter::typeid)] DeviceConfig config; DeviceSettings(ra::device_settings& args) @@ -470,21 +622,28 @@ public: }; -[JsonObject(ItemRequired = Required::Always)] public ref class DriverConfig { public: literal double WriteDelayMs = ra::WRITE_DELAY; - literal String^ Key = "Driver settings"; + initonly static JsonSerializerOptions^ DefaultOptions = DefaultSerializerOptions(); + [JsonInclude] + [JsonConverter(NullToEmptyStringConverter::typeid)] String^ version = RA_VER_STRING; + [JsonInclude] + [JsonConverter(DeviceConfigConverter::typeid)] DeviceConfig defaultDeviceConfig; + [JsonInclude] + [JsonConverter(NullToEmptyListConverter<Profile^>::typeid)] List<Profile^>^ profiles; - [NonSerialized] + [JsonIgnore] List<ManagedAccel^>^ accels; + [JsonInclude] + [JsonConverter(NullToEmptyListConverter<DeviceSettings^>::typeid)] List<DeviceSettings^>^ devices; void Activate() @@ -579,69 +738,22 @@ public: } } - JObject^ ToJObject() - { - auto dataQueue = gcnew Queue<array<float>^>(); - auto empty = Array::Empty<float>(); - - for each (auto prof in profiles) { - if (prof->argsX.mode == AccelMode::lut) { - // data->Length is fixed for interop, - // temporary resize avoids serializing a bunch of zeros - Array::Resize(prof->argsX.data, prof->argsX.length); - } - else { - // table data may be used internally in any mode, - // so hide it when it's not needed for deserialization - dataQueue->Enqueue(prof->argsX.data); - prof->argsX.data = empty; - } - - if (prof->argsY.mode == AccelMode::lut) { - Array::Resize(prof->argsY.data, prof->argsY.length); - } - else { - dataQueue->Enqueue(prof->argsY.data); - prof->argsY.data = empty; - } - } - - JObject^ jObject = JObject::FromObject(this); - String^ capModes = String::Join(" | ", Enum::GetNames(ClassicCapMode::typeid)); - String^ accelModes = String::Join(" | ", Enum::GetNames(AccelMode::typeid)); - jObject->AddFirst(gcnew JProperty("### Cap modes (applies to classic only) ###", capModes)); - jObject->AddFirst(gcnew JProperty("### Accel modes ###", accelModes)); - - for each (auto prof in profiles) { - if (prof->argsX.mode == AccelMode::lut) { - Array::Resize(prof->argsX.data, ra::LUT_RAW_DATA_CAPACITY); - } - else { - prof->argsX.data = dataQueue->Dequeue(); - } - - if (prof->argsY.mode == AccelMode::lut) { - Array::Resize(prof->argsY.data, ra::LUT_RAW_DATA_CAPACITY); - } - else { - prof->argsY.data = dataQueue->Dequeue(); - } - } - - return jObject; - } - String^ ToJSON() { - return ToJObject()->ToString(); + using namespace System::Text; + StringBuilder^ sb = gcnew StringBuilder(); + sb->AppendFormat( + "/*\n* Accel modes: {0}\n* Cap modes (classic only): {1}\n*/\n", + String::Join(" | ", Enum::GetNames(AccelMode::typeid)), + String::Join(" | ", Enum::GetNames(ClassicCapMode::typeid))); + sb->Append(JsonSerializer::Serialize(this, DefaultOptions)); + return sb->ToString(); } // returns (config, null) or (null, error message) static Tuple<DriverConfig^, String^>^ Convert(String^ json) { - auto jss = gcnew JsonSerializerSettings(); - jss->DefaultValueHandling = DefaultValueHandling::Populate; - auto cfg = JsonConvert::DeserializeObject<DriverConfig^>(json, jss); + auto cfg = JsonSerializer::Deserialize<DriverConfig^>(json, DefaultOptions); if (cfg == nullptr) throw gcnew JsonException("invalid JSON"); auto message = cfg->Errors(); @@ -673,9 +785,6 @@ public: } auto cfg = gcnew DriverConfig(); - cfg->profiles = gcnew List<Profile^>(); - cfg->accels = gcnew List<ManagedAccel^>(); - cfg->devices = gcnew List<DeviceSettings^>(); auto* byte_ptr = bytes.get(); ra::io_base* base_data = reinterpret_cast<ra::io_base*>(byte_ptr); @@ -704,9 +813,6 @@ public: static DriverConfig^ FromProfile(Profile^ prof) { auto cfg = gcnew DriverConfig(); - cfg->profiles = gcnew List<Profile^>(); - cfg->accels = gcnew List<ManagedAccel^>(); - cfg->devices = gcnew List<DeviceSettings^>(); cfg->profiles->Add(prof); cfg->accels->Add(gcnew ManagedAccel(prof)); @@ -729,7 +835,21 @@ public: } } -private: - DriverConfig() {} -}; + DriverConfig() + { + profiles = gcnew List<Profile^>(); + accels = gcnew List<ManagedAccel^>(); + devices = gcnew List<DeviceSettings^>(); + defaultDeviceConfig.Init(default_device_settings.config); + } +private: + static JsonSerializerOptions^ DefaultSerializerOptions() + { + JsonSerializerOptions^ options = gcnew JsonSerializerOptions(); + options->WriteIndented = true; + options->ReadCommentHandling = JsonCommentHandling::Skip; + options->AllowTrailingCommas = true; + return options; + } +}; |