summaryrefslogtreecommitdiff
path: root/wrapper/wrapper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'wrapper/wrapper.cpp')
-rw-r--r--wrapper/wrapper.cpp874
1 files changed, 467 insertions, 407 deletions
diff --git a/wrapper/wrapper.cpp b/wrapper/wrapper.cpp
index 4e6c4f1..fba66fa 100644
--- a/wrapper/wrapper.cpp
+++ b/wrapper/wrapper.cpp
@@ -15,38 +15,36 @@ using namespace Newtonsoft::Json::Linq;
namespace ra = rawaccel;
-ra::settings default_settings;
+ra::driver_settings default_driver_settings;
+ra::device_settings default_device_settings;
-[JsonConverter(Converters::StringEnumConverter::typeid)]
-public enum class AccelMode
+public ref struct VersionHelper
{
- classic, jump, natural, motivity, power, lut, noaccel
+ literal String^ VersionString = RA_VER_STRING;
+
+ static Version^ ValidOrThrow()
+ {
+ try {
+ ra::version_t v = ra::valid_version_or_throw();
+ return gcnew Version(v.major, v.minor, v.patch, 0);
+ }
+ catch (const ra::error& e) {
+ throw gcnew InteropException(e);
+ }
+ }
};
[JsonConverter(Converters::StringEnumConverter::typeid)]
-public enum class SpacedTableMode
+public enum class AccelMode
{
- off, binlog, linear
+ classic, jump, natural, motivity, power, lut, noaccel
};
-[StructLayout(LayoutKind::Sequential)]
-public value struct SpacedTableArgs
-{
- [JsonIgnore]
- SpacedTableMode mode;
-
- [MarshalAs(UnmanagedType::U1)]
- bool transfer;
-
- [MarshalAs(UnmanagedType::U1)]
- unsigned char partitions;
-
- short num;
- double start;
- double stop;
+[JsonConverter(Converters::StringEnumConverter::typeid)]
+public enum class ClassicCapMode {
+ in_out, input, output
};
-
generic <typename T>
[StructLayout(LayoutKind::Sequential)]
public value struct Vec2
@@ -56,442 +54,317 @@ public value struct Vec2
};
[StructLayout(LayoutKind::Sequential)]
-public value struct TableArgs
-{
- [JsonProperty("Whether points affect velocity (true) or sensitivity (false)")]
- [MarshalAs(UnmanagedType::U1)]
- bool velocity;
-
- [JsonIgnore]
- int length;
-
- [MarshalAs(UnmanagedType::ByValArray, SizeConst = ra::ARB_LUT_CAPACITY)]
- array<Vec2<float>>^ points;
-
- virtual bool Equals(Object^ ob) override {
- if (ob->GetType() == this->GetType()) {
- TableArgs^ other = (TableArgs^)ob;
-
- if (this->length != other->length) return false;
- if (this->points == other->points) return true;
-
- if (unsigned(length) >= ra::ARB_LUT_CAPACITY ||
- points == nullptr ||
- other->points == nullptr) {
- throw gcnew InteropException("invalid table args");
- }
-
- for (int i = 0; i < length; i++) {
- if (points[i].x != other->points[i].x ||
- points[i].y != other->points[i].y)
- return false;
- }
-
- return true;
- }
- else {
- return false;
- }
- }
-
- virtual int GetHashCode() override {
- return points->GetHashCode() ^ length.GetHashCode();
- }
-};
-
-[StructLayout(LayoutKind::Sequential)]
public value struct AccelArgs
{
+ literal int MaxLutPoints = ra::LUT_POINTS_CAPACITY;
+
AccelMode mode;
+ [JsonProperty("Gain / Velocity")]
[MarshalAs(UnmanagedType::U1)]
- bool legacy;
+ bool gain;
double offset;
- double cap;
- double accelClassic;
+ double acceleration;
double decayRate;
double growthRate;
double motivity;
- double power;
+ double exponentClassic;
double scale;
double weight;
- double exponent;
+ double exponentPower;
double limit;
double midpoint;
double smooth;
- [JsonProperty(Required = Required::Default)]
- SpacedTableArgs spacedTableArgs;
+ [JsonProperty("Cap / Jump")]
+ Vec2<double> cap;
- TableArgs tableData;
-};
+ [JsonProperty("Cap mode")]
+ ClassicCapMode capMode;
-[StructLayout(LayoutKind::Sequential)]
-public value struct DomainArgs
-{
- Vec2<double> domainXY;
- double lpNorm;
+ [JsonIgnore]
+ int length;
+
+ [MarshalAs(UnmanagedType::ByValArray, SizeConst = ra::LUT_RAW_DATA_CAPACITY)]
+ array<float>^ data;
+
+ [OnDeserialized]
+ void OnDeserializedMethod(StreamingContext context)
+ {
+ // data->Length must match SizeConst when marshalling
+ length = data->Length;
+ array<float>::Resize(data, ra::LUT_RAW_DATA_CAPACITY);
+ }
};
[JsonObject(ItemRequired = Required::Always)]
[StructLayout(LayoutKind::Sequential, CharSet = CharSet::Unicode)]
-public ref struct DriverSettings
+public ref struct Profile
{
- literal double WriteDelayMs = ra::WRITE_DELAY;
- literal String^ Key = "Driver settings";
-
- [JsonProperty("Degrees of rotation")]
- double rotation;
-
- [JsonProperty("Degrees of angle snapping")]
- double snap;
+ [MarshalAs(UnmanagedType::ByValTStr, SizeConst = ra::MAX_NAME_LEN)]
+ System::String^ name;
- [JsonProperty("Use x as whole/combined accel")]
+ [JsonProperty("Whole/combined accel (set false for 'by component' mode)")]
[MarshalAs(UnmanagedType::U1)]
bool combineMagnitudes;
- double dpi;
-
- [JsonIgnore]
- double minimumSpeed;
- [JsonProperty("Input Speed Cap")]
- double maximumSpeed;
-
- [JsonProperty("Accel parameters")]
- Vec2<AccelArgs> args;
-
- [JsonProperty("Sensitivity multipliers")]
- Vec2<double> sensitivity;
-
- [JsonProperty("Negative directional multipliers")]
- Vec2<double> directionalMultipliers;
+ double lpNorm;
[JsonProperty("Stretches domain for horizontal vs vertical inputs")]
- DomainArgs domainArgs;
-
+ Vec2<double> domainXY;
[JsonProperty("Stretches accel range for horizontal vs vertical inputs")]
Vec2<double> rangeXY;
- [JsonProperty(Required = Required::Default)]
- double minimumTime;
+ [JsonProperty("Sensitivity multiplier")]
+ double sensitivity;
- [JsonProperty(Required = Required::Default)]
- double maximumTime;
+ [JsonProperty("Y/X sensitivity ratio (vertical sens multiplier)")]
+ double yxSensRatio;
- [JsonProperty("Ignore devices with matching ID")]
- [MarshalAs(UnmanagedType::U1)]
- bool ignore;
-
- [JsonProperty("Device ID")]
- [MarshalAs(UnmanagedType::ByValTStr, SizeConst = ra::MAX_DEV_ID_LEN)]
- String^ deviceID = "";
+ [JsonProperty("Whole or horizontal accel parameters")]
+ AccelArgs argsX;
+ [JsonProperty("Vertical accel parameters")]
+ AccelArgs argsY;
- bool ShouldSerializeminimumTime()
- {
- return minimumTime != ra::DEFAULT_TIME_MIN;
- }
-
- bool ShouldSerializemaximumTime()
- {
- return maximumTime != ra::DEFAULT_TIME_MAX;
- }
-
- DriverSettings()
- {
- Marshal::PtrToStructure(IntPtr(&default_settings), this);
- }
+ [JsonIgnore]
+ double minimumSpeed;
+ [JsonProperty("Input Speed Cap")]
+ double maximumSpeed;
-private:
- [OnDeserialized]
- void OnDeserializedMethod(StreamingContext context)
- {
- args.x.tableData.length = args.x.tableData.points->Length;
- args.y.tableData.length = args.y.tableData.points->Length;
+ [JsonProperty("Negative directional multipliers")]
+ Vec2<double> directionalMultipliers;
- array<Vec2<float>>::Resize(args.x.tableData.points, ra::ARB_LUT_CAPACITY);
- array<Vec2<float>>::Resize(args.y.tableData.points, ra::ARB_LUT_CAPACITY);
- }
+ [JsonProperty("Degrees of rotation")]
+ double rotation;
- [OnSerializing]
- void OnSerializingMethod(StreamingContext context)
- {
- array<Vec2<float>>::Resize(args.x.tableData.points, args.x.tableData.length);
- array<Vec2<float>>::Resize(args.y.tableData.points, args.y.tableData.length);
- }
+ [JsonProperty("Degrees of angle snapping")]
+ double snap;
- [OnSerialized]
- void OnSerializedMethod(StreamingContext context)
+ Profile(ra::profile& args)
{
- array<Vec2<float>>::Resize(args.x.tableData.points, ra::ARB_LUT_CAPACITY);
- array<Vec2<float>>::Resize(args.y.tableData.points, ra::ARB_LUT_CAPACITY);
+ Marshal::PtrToStructure(IntPtr(&args), this);
}
+ Profile() :
+ Profile(default_driver_settings.prof) {}
};
[JsonObject(ItemRequired = Required::Always)]
-public ref struct LutBase
-{
- [JsonConverter(Converters::StringEnumConverter::typeid)]
- enum class Mode
- {
- logarithmic, linear
- } mode;
-
- virtual void SetArgs(AccelArgs%) {}
- virtual void SetData(ra::accel_union&) {}
-};
-
+[StructLayout(LayoutKind::Sequential)]
+public value struct DeviceConfig {
+ [MarshalAs(UnmanagedType::U1)]
+ bool disable;
-[JsonObject(ItemRequired = Required::Always)]
-public ref struct SpacedLut abstract : public LutBase
-{
- [JsonProperty("Whether points affect velocity (true) or sensitivity (false)")]
- bool transfer;
+ [MarshalAs(UnmanagedType::U1)]
+ [JsonProperty(Required = Required::Default)]
+ bool setExtraInfo;
- double start;
- double stop;
- array<float>^ data;
+ [JsonProperty("DPI (normalizes sens to 1000dpi and converts input speed unit: counts/ms -> in/s)")]
+ int dpi;
- void SetArgsBase(AccelArgs% args)
- {
- args.spacedTableArgs.transfer = transfer;
- args.spacedTableArgs.start = start;
- args.spacedTableArgs.stop = stop;
- }
+ [JsonProperty("Polling rate Hz (keep at 0 for automatic adjustment)")]
+ int pollingRate;
+
+ [ComponentModel::DefaultValue(ra::DEFAULT_TIME_MIN)]
+ [JsonProperty(Required = Required::Default)]
+ double minimumTime;
- void SetDataBase(ra::accel_union& accel)
- {
- if (size_t(data->LongLength) > ra::SPACED_LUT_CAPACITY) {
- throw gcnew InteropException("data is too large");
- }
- }
-};
+ [ComponentModel::DefaultValue(ra::DEFAULT_TIME_MAX)]
+ [JsonProperty(Required = Required::Default)]
+ double maximumTime;
-[JsonObject(ItemRequired = Required::Always)]
-public ref struct LinearLut sealed : public SpacedLut
-{
- LinearLut()
+ bool ShouldSerializesetExtraInfo()
{
+ return setExtraInfo == true;
}
- LinearLut(const ra::linear_lut& table)
+ bool ShouldSerializeminimumTime()
{
- 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);
+ return minimumTime != ra::DEFAULT_TIME_MIN;
}
- virtual void SetArgs(AccelArgs% args) override
+ bool ShouldSerializemaximumTime()
{
- SetArgsBase(args);
- args.spacedTableArgs.num = data->Length;
- args.spacedTableArgs.mode = SpacedTableMode::linear;
+ return maximumTime != ra::DEFAULT_TIME_MAX;
}
- virtual void SetData(ra::accel_union& accel) override
+ void Init(const ra::device_config& cfg)
{
- SetDataBase(accel);
- pin_ptr<float> pdata = &data[0];
- std::memcpy(&accel.lin_lut.data, pdata, sizeof(float) * data->Length);
+ disable = cfg.disable;
+ setExtraInfo = cfg.set_extra_info;
+ dpi = cfg.dpi;
+ pollingRate = cfg.polling_rate;
+ minimumTime = cfg.clamp.min;
+ maximumTime = cfg.clamp.max;
}
};
[JsonObject(ItemRequired = Required::Always)]
-public ref struct BinLogLut sealed : public SpacedLut
+[StructLayout(LayoutKind::Sequential, CharSet = CharSet::Unicode)]
+public ref struct DeviceSettings
{
- short num;
+ [MarshalAs(UnmanagedType::ByValTStr, SizeConst = ra::MAX_NAME_LEN)]
+ String^ name;
- BinLogLut()
- {
- }
+ [MarshalAs(UnmanagedType::ByValTStr, SizeConst = ra::MAX_NAME_LEN)]
+ String^ profile;
- 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)));
+ [MarshalAs(UnmanagedType::ByValTStr, SizeConst = ra::MAX_DEV_ID_LEN)]
+ String^ id;
- pin_ptr<float> pdata = &data[0];
- std::memcpy(pdata, &table.data, sizeof(float) * data->Length);
- }
+ DeviceConfig config;
- virtual void SetArgs(AccelArgs% args) override
+ DeviceSettings(ra::device_settings& args)
{
- SetArgsBase(args);
- args.spacedTableArgs.num = num;
- args.spacedTableArgs.mode = SpacedTableMode::binlog;
+ Marshal::PtrToStructure(IntPtr(&args), this);
}
- 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");
- }
-
- pin_ptr<float> pdata = &data[0];
- std::memcpy(&accel.log_lut.data, pdata, sizeof(float) * data->Length);
- }
+ DeviceSettings() :
+ DeviceSettings(default_device_settings) {}
};
-public ref struct RaConvert {
- static DriverSettings^ Settings(String^ json)
- {
- return NonNullable<DriverSettings^>(json);
- }
+public ref class ProfileErrors
+{
+ List<String^>^ tmp;
+ bool single;
- 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);
- }
+ delegate void MsgHandler(const char*);
- static LutBase^ Table(String^ json)
+ void Add(const char* msg)
{
- 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);
- default:
- throw gcnew NotImplementedException();
- }
+ tmp->Add(gcnew String(msg));
}
- static String^ Table(LutBase^ lut)
+public:
+ ref struct SingleProfileErrors
{
- auto serializerSettings = gcnew JsonSerializerSettings();
- return JsonConvert::SerializeObject(
- lut, lut->GetType(), Formatting::Indented, serializerSettings);
+ Profile^ prof;
+ array<String^>^ messages;
+ int lastX;
+ int lastY;
};
- generic <typename T>
- static T NonNullable(String^ json)
+ List<SingleProfileErrors^>^ list;
+
+ ProfileErrors(List<Profile^>^ profiles)
{
- T obj = JsonConvert::DeserializeObject<T>(json);
- if (obj == nullptr) throw gcnew JsonException("invalid JSON");
- return obj;
+ single = profiles->Count == 1;
+ list = gcnew List<SingleProfileErrors^>();
+ tmp = gcnew List<String^>();
+ MsgHandler^ del = gcnew MsgHandler(this, &ProfileErrors::Add);
+ GCHandle gch = GCHandle::Alloc(del);
+ auto fp = static_cast<void (*)(const char*)>(
+ Marshal::GetFunctionPointerForDelegate(del).ToPointer());
+ ra::profile* native_ptr = new ra::profile();
+
+ for each (auto prof in profiles) {
+ Marshal::StructureToPtr(prof, IntPtr(native_ptr), false);
+ auto [last_x, last_y, _] = ra::valid(*native_ptr, fp);
+
+ if (tmp->Count != 0) {
+ auto singleErrors = gcnew SingleProfileErrors();
+ singleErrors->messages = tmp->ToArray();
+ singleErrors->lastX = last_x;
+ singleErrors->lastY = last_y;
+ singleErrors->prof = prof;
+ list->Add(singleErrors);
+ tmp->Clear();
+ }
+ }
+
+ tmp = nullptr;
+ gch.Free();
+ delete native_ptr;
}
- generic <typename T>
- static T NonNullable(JObject^ jObject)
+ bool Empty()
{
- T obj = jObject->ToObject<T>();
- if (obj == nullptr) throw gcnew JsonException("invalid JSON");
- return obj;
+ return list->Count == 0;
}
-};
-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)
+ virtual String^ ToString() override
{
- if (settingsJson) {
- baseSettings = RaConvert::Settings(settingsJson);
- }
- else {
- baseSettings = gcnew DriverSettings();
- }
-
- if (xTableJson || yTableJson) {
- baseSettings->combineMagnitudes = !byComponent;
- }
+ Text::StringBuilder^ sb = gcnew Text::StringBuilder();
- if (xTableJson) {
- tables.x = RaConvert::Table(xTableJson);
- tables.x->SetArgs(baseSettings->args.x);
- }
+ for each (auto elem in list) {
+ if (!single) {
+ sb->AppendFormat("profile: {0}\n", elem->prof->name);
+ }
- if (yTableJson) {
- if (Object::ReferenceEquals(yTableJson, xTableJson)) {
- tables.y = tables.x;
+ auto msgs = elem->messages;
+ if (elem->prof->combineMagnitudes) {
+ for (int i = 0; i < elem->lastX; i++) {
+ sb->AppendFormat("\t{0}\n", msgs[i]);
+ }
}
else {
- tables.y = RaConvert::Table(yTableJson);
+ for (int i = 0; i < elem->lastX; i++) {
+ sb->AppendFormat("\tx: {0}\n", msgs[i]);
+ }
+ for (int i = elem->lastX; i < elem->lastY; i++) {
+ sb->AppendFormat("\ty: {0}\n", msgs[i]);
+ }
}
- tables.y->SetArgs(baseSettings->args.y);
+ for (int i = elem->lastY; i < msgs->Length; i++) {
+ sb->AppendFormat("\t{0}\n", msgs[i]);
+ }
}
- }
+ return sb->ToString();
+ }
};
-public ref class SettingsErrors
+public ref class DeviceErrors
{
-public:
-
- List<String^>^ list;
- int lastX;
- int lastY;
+ List<String^>^ tmp;
+ bool single;
delegate void MsgHandler(const char*);
void Add(const char* msg)
{
- list->Add(gcnew String(msg));
+ tmp->Add(gcnew String(msg));
}
- SettingsErrors(ExtendedSettings^ settings) :
- SettingsErrors(settings->baseSettings) {}
+public:
+ ref struct SingleDeviceErrors
+ {
+ DeviceSettings^ settings;
+ array<String^>^ messages;
+ };
+
+ List<SingleDeviceErrors^>^ list;
- SettingsErrors(DriverSettings^ settings)
+ DeviceErrors(List<DeviceSettings^>^ devSettings)
{
- MsgHandler^ del = gcnew MsgHandler(this, &SettingsErrors::Add);
+ single = devSettings->Count == 1;
+ list = gcnew List<SingleDeviceErrors^>();
+ tmp = gcnew List<String^>();
+ MsgHandler^ del = gcnew MsgHandler(this, &DeviceErrors::Add);
GCHandle gch = GCHandle::Alloc(del);
auto fp = static_cast<void (*)(const char*)>(
Marshal::GetFunctionPointerForDelegate(del).ToPointer());
+ ra::device_settings* native_ptr = new ra::device_settings();
+
+ for each (auto dev in devSettings) {
+ Marshal::StructureToPtr(dev, IntPtr(native_ptr), false);
+ ra::valid(*native_ptr, fp);
+
+ if (tmp->Count != 0) {
+ auto singleErrors = gcnew SingleDeviceErrors();
+ singleErrors->messages = tmp->ToArray();
+ singleErrors->settings = dev;
+ list->Add(singleErrors);
+ tmp->Clear();
+ }
+ }
- ra::settings* args_ptr = new ra::settings();
- Marshal::StructureToPtr(settings, (IntPtr)args_ptr, false);
-
- list = gcnew List<String^>();
- auto [last_x, last_y, _] = ra::valid(*args_ptr, fp);
- lastX = last_x;
- lastY = last_y;
-
+ tmp = nullptr;
gch.Free();
- delete args_ptr;
+ delete native_ptr;
}
bool Empty()
@@ -503,36 +376,52 @@ public:
{
Text::StringBuilder^ sb = gcnew Text::StringBuilder();
- for each (auto s in list->GetRange(0, lastX))
- {
- sb->AppendFormat("x: {0}\n", s);
- }
- for each (auto s in list->GetRange(lastX, lastY - lastX))
- {
- sb->AppendFormat("y: {0}\n", s);
- }
- for each (auto s in list->GetRange(lastY, list->Count - lastY))
- {
- sb->AppendLine(s);
+ for each (auto elem in list) {
+ if (!single) {
+ sb->AppendFormat("device: {0}\n", elem->settings->id);
+ if (!String::IsNullOrWhiteSpace(elem->settings->name)) {
+ sb->AppendFormat(" name: {0}\n", elem->settings->name);
+ }
+ }
+
+ for each (auto msg in elem->messages) {
+ sb->AppendFormat("\tx: {0}\n", msg);
+ }
}
return sb->ToString();
}
};
-struct instance_t {
- ra::io_t data;
- vec2<ra::accel_invoker> inv;
+struct accel_instance_t {
+ ra::modifier mod;
+ ra::driver_settings settings;
+
+ accel_instance_t() = default;
+
+ accel_instance_t(ra::driver_settings& args) :
+ settings(args),
+ mod(args) {}
+
+ void init(Profile^ args)
+ {
+ Marshal::StructureToPtr(args, IntPtr(&settings.prof), false);
+ ra::init_data(settings);
+ mod = { settings };
+ }
};
public ref class ManagedAccel
{
- instance_t* const instance = new instance_t();
-
+ accel_instance_t* const instance = new accel_instance_t();
public:
+
ManagedAccel() {}
- ManagedAccel(ExtendedSettings^ settings)
+ ManagedAccel(ra::driver_settings& settings) :
+ instance(new accel_instance_t(settings)) {}
+
+ ManagedAccel(Profile^ settings)
{
Settings = settings;
}
@@ -547,95 +436,266 @@ public:
delete instance;
}
- Tuple<double, double>^ Accelerate(int x, int y, double time)
+ Tuple<double, double>^ Accelerate(int x, int y, double dpi_factor, double time)
{
vec2d in_out_vec = {
(double)x,
(double)y
};
- instance->data.mod.modify(in_out_vec, instance->inv, time);
+ instance->mod.modify(in_out_vec, instance->settings, dpi_factor, time);
return gcnew Tuple<double, double>(in_out_vec.x, in_out_vec.y);
}
+ property Profile^ Settings
+ {
+ Profile^ get()
+ {
+ return gcnew Profile(instance->settings.prof);
+ }
+
+ void set(Profile^ val)
+ {
+ instance->init(val);
+ }
+
+ }
+
+ ra::driver_settings GetSettings()
+ {
+ return instance->settings;
+ }
+
+};
+
+
+[JsonObject(ItemRequired = Required::Always)]
+public ref class DriverConfig {
+public:
+ literal double WriteDelayMs = ra::WRITE_DELAY;
+ literal int MaxProfiles = ra::DRIVER_CAPACITY;
+ literal int MaxDevices = ra::DEVICE_CAPACITY;
+ literal String^ Key = "Driver settings";
+
+ String^ version = RA_VER_STRING;
+
+ DeviceConfig defaultDeviceConfig;
+
+ List<Profile^>^ profiles;
+
+ [NonSerialized]
+ List<ManagedAccel^>^ accels;
+
+ List<DeviceSettings^>^ devices;
+
void Activate()
{
+ ra::io_t* data = static_cast<ra::io_t*>(malloc(sizeof(ra::io_t)));
+
+ if (!data) throw gcnew Exception("bad alloc");
+
+ data->default_dev_cfg.disable = defaultDeviceConfig.disable;
+ data->default_dev_cfg.set_extra_info = defaultDeviceConfig.setExtraInfo;
+ data->default_dev_cfg.dpi = defaultDeviceConfig.dpi;
+ data->default_dev_cfg.polling_rate = defaultDeviceConfig.pollingRate;
+ data->default_dev_cfg.clamp.min = defaultDeviceConfig.minimumTime;
+ data->default_dev_cfg.clamp.max = defaultDeviceConfig.maximumTime;
+
+ data->driver_data_size = profiles->Count;
+ data->device_data_size = devices->Count;
+
+ for (auto i = 0; i < profiles->Count; i++) {
+ auto& drv_settings = data->driver_data[i];
+ drv_settings = accels[i]->GetSettings();
+ }
+
+ for (auto i = 0; i < devices->Count; i++) {
+ auto& dev_settings = data->device_data[i];
+ Marshal::StructureToPtr(devices[i], IntPtr(&dev_settings), false);
+ }
+
try {
- ra::write(instance->data);
+ ra::write(*data);
+ free(data);
}
catch (const ra::error& e) {
+ free(data);
throw gcnew InteropException(e);
}
}
- property ExtendedSettings^ Settings
+ // returns null or a joined list of error messages
+ String^ Errors()
{
- ExtendedSettings^ get()
- {
- auto settings = gcnew ExtendedSettings();
- Marshal::PtrToStructure(IntPtr(&instance->data.args), settings->baseSettings);
- settings->tables.x = extract(instance->data.args.argsv.x.spaced_args.mode,
- instance->data.mod.accel.x);
- settings->tables.y = extract(instance->data.args.argsv.y.spaced_args.mode,
- instance->data.mod.accel.y);
- return settings;
+ Text::StringBuilder^ sb = gcnew Text::StringBuilder();
+
+ if (profiles->Count > MaxProfiles) {
+ sb->AppendFormat("Number of profiles ({0}) exceeds max ({1})\n", profiles->Count, MaxProfiles);
}
- void set(ExtendedSettings^ val)
- {
- Marshal::StructureToPtr(val->baseSettings, IntPtr(&instance->data.args), false);
- instance->data.mod = { instance->data.args };
- instance->inv = ra::invokers(instance->data.args);
+ if (devices->Count > MaxDevices) {
+ sb->AppendFormat("Number of devices ({0}) exceeds max ({1})\n", devices->Count, MaxDevices);
+ }
- if (val->tables.x) {
- val->tables.x->SetData(instance->data.mod.accel.x);
- }
+ ProfileErrors^ profErrors = gcnew ProfileErrors(profiles);
+ if (!profErrors->Empty()) {
+ sb->Append(profErrors->ToString());
+ }
+
+ DeviceSettings^ defaultDev = gcnew DeviceSettings();
+ defaultDev->config = defaultDeviceConfig;
+ defaultDev->id = "Default";
+ devices->Add(defaultDev);
- if (val->tables.y) {
- val->tables.y->SetData(instance->data.mod.accel.y);
- }
+ DeviceErrors^ devErrors = gcnew DeviceErrors(devices);
+ if (!devErrors->Empty()) {
+ sb->Append(profErrors->ToString());
}
+ devices->RemoveAt(devices->Count - 1);
+
+ if (sb->Length == 0) {
+ return nullptr;
+ }
+ else {
+ return sb->ToString();
+ }
}
- static ManagedAccel^ GetActive()
+ JObject^ ToJObject()
{
- try {
- auto active = gcnew ManagedAccel();
- ra::read(active->instance->data);
- active->instance->inv = ra::invokers(active->instance->data.args);
- return active;
+ 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;
+ }
}
- catch (const ra::error& e) {
- throw gcnew InteropException(e);
+
+ 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;
}
-private:
- LutBase^ extract(ra::spaced_lut_mode mode, ra::accel_union& au)
+ String^ ToJSON()
{
- switch (mode) {
- case ra::spaced_lut_mode::off: return nullptr;
- case ra::spaced_lut_mode::linear: return gcnew LinearLut(au.lin_lut);
- case ra::spaced_lut_mode::binlog: return gcnew BinLogLut(au.log_lut);
- default: throw gcnew NotImplementedException();
- }
+ return ToJObject()->ToString();
}
-};
-public ref struct VersionHelper
-{
- literal String^ VersionString = RA_VER_STRING;
+ // 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);
+ if (cfg == nullptr) throw gcnew JsonException("invalid JSON");
- static Version^ ValidOrThrow()
+ auto message = cfg->Errors();
+ if (message != nullptr) {
+ return gcnew Tuple<DriverConfig^, String^>(nullptr, message);
+ }
+ else {
+ cfg->accels = gcnew List<ManagedAccel^>();
+
+ if (cfg->profiles->Count == 0) {
+ cfg->profiles->Add(gcnew Profile());
+ }
+
+ for each (auto prof in cfg->profiles) {
+ cfg->accels->Add(gcnew ManagedAccel(prof));
+ }
+ return gcnew Tuple<DriverConfig^, String^>(cfg, nullptr);
+ }
+ }
+
+ static DriverConfig^ GetActive()
{
+ ra::io_t* data = static_cast<ra::io_t*>(malloc(sizeof(ra::io_t)));
+
+ if (!data) throw gcnew Exception("io_t alloc failed");
+
try {
- ra::version_t v = ra::valid_version_or_throw();
- return gcnew Version(v.major, v.minor, v.patch, 0);
+ ra::read(*data);
}
catch (const ra::error& e) {
+ free(data);
throw gcnew InteropException(e);
+ }
+
+ auto cfg = gcnew DriverConfig();
+ cfg->profiles = gcnew List<Profile^>();
+ cfg->accels = gcnew List<ManagedAccel^>();
+ cfg->devices = gcnew List<DeviceSettings^>();
+
+ for (auto i = 0u; i < data->driver_data_size; i++) {
+ auto& drv_settings = data->driver_data[i];
+ cfg->profiles->Add(gcnew Profile(drv_settings.prof));
+ cfg->accels->Add(gcnew ManagedAccel(drv_settings));
}
+
+ for (auto i = 0u; i < data->device_data_size; i++) {
+ auto& dev_settings = data->device_data[i];
+ cfg->devices->Add(gcnew DeviceSettings(dev_settings));
+ }
+
+ cfg->defaultDeviceConfig.Init(data->default_dev_cfg);
+
+ free(data);
+ return cfg;
+ }
+
+ static DriverConfig^ GetDefault()
+ {
+ auto cfg = gcnew DriverConfig();
+ cfg->profiles = gcnew List<Profile^>();
+ cfg->accels = gcnew List<ManagedAccel^>();
+ cfg->devices = gcnew List<DeviceSettings^>();
+
+ cfg->profiles->Add(gcnew Profile());
+ cfg->accels->Add(gcnew ManagedAccel(default_driver_settings));
+ cfg->defaultDeviceConfig.Init(default_device_settings.config);
+ return cfg;
}
+
+private:
+ DriverConfig() {}
};
+