#pragma once #include #include #include #include #include #include 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; namespace ra = rawaccel; ra::settings default_settings; [JsonConverter(Converters::StringEnumConverter::typeid)] public enum class AccelMode { classic, jump, natural, power, motivity, noaccel }; public enum class TableMode { off, binlog, linear }; [StructLayout(LayoutKind::Sequential)] public value struct TableArgs { [JsonIgnore] TableMode mode; [MarshalAs(UnmanagedType::U1)] bool transfer; [MarshalAs(UnmanagedType::U1)] unsigned char partitions; short num; double start; double stop; }; generic [StructLayout(LayoutKind::Sequential)] public value struct Vec2 { T x; T y; }; [StructLayout(LayoutKind::Sequential)] public value struct AccelArgs { AccelMode mode; [MarshalAs(UnmanagedType::U1)] bool legacy; [JsonProperty(Required = Required::Default)] TableArgs lutArgs; double offset; double cap; double accelClassic; double accelNatural; double accelMotivity; double motivity; double power; double scale; double weight; double exponent; double limit; double midpoint; double smooth; }; [StructLayout(LayoutKind::Sequential)] public value struct DomainArgs { Vec2 domainXY; double lpNorm; }; [JsonObject(ItemRequired = Required::Always)] [StructLayout(LayoutKind::Sequential, CharSet = CharSet::Unicode)] public ref struct DriverSettings { literal double WriteDelayMs = ra::WRITE_DELAY; literal String^ Key = "Driver settings"; [JsonProperty("Degrees of rotation")] double rotation; [JsonProperty("Degrees of angle snapping")] double snap; [JsonProperty("Use x as whole/combined accel")] [MarshalAs(UnmanagedType::U1)] bool combineMagnitudes; double dpi; double speedCap; [JsonProperty("Accel parameters")] Vec2 args; [JsonProperty("Sensitivity multipliers")] Vec2 sensitivity; [JsonProperty("Negative directional multipliers")] Vec2 directionalMultipliers; [JsonProperty("Stretches domain for horizontal vs vertical inputs")] DomainArgs domainArgs; [JsonProperty("Stretches accel range for horizontal vs vertical inputs")] Vec2 rangeXY; [JsonProperty(Required = Required::Default)] double minimumTime; [JsonProperty("Device ID")] [MarshalAs(UnmanagedType::ByValTStr, SizeConst = ra::MAX_DEV_ID_LEN)] String^ deviceID = ""; bool ShouldSerializeminimumTime() { return minimumTime != ra::DEFAULT_TIME_MIN; } DriverSettings() { Marshal::PtrToStructure(IntPtr(&default_settings), this); } void ToFile(String^ path) { using namespace Newtonsoft::Json::Linq; 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)); } static DriverSettings^ FromFile(String^ path) { if (!File::Exists(path)) { throw gcnew FileNotFoundException( String::Format("Settings file not found at {0}", path)); } auto settings = JsonConvert::DeserializeObject( File::ReadAllText(path)); if (settings == nullptr) { throw gcnew JsonException(String::Format("{0} contains invalid JSON", path)); } return settings; } }; 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 class SettingsErrors { public: List^ list; int countX; int countY; delegate void MsgHandler(const char*); void Add(const char* msg) { list->Add(msclr::interop::marshal_as(msg)); } SettingsErrors(DriverSettings^ settings) { MsgHandler^ del = gcnew MsgHandler(this, &SettingsErrors::Add); GCHandle gch = GCHandle::Alloc(del); auto fp = static_cast( Marshal::GetFunctionPointerForDelegate(del).ToPointer()); ra::settings args; Marshal::StructureToPtr(settings, (IntPtr)&args, false); list = gcnew List(); auto [cx, cy, _] = ra::valid(args, fp); countX = cx; countY = cy; gch.Free(); } bool Empty() { return list->Count == 0; } virtual String^ ToString() override { Text::StringBuilder^ sb = gcnew Text::StringBuilder(); for each (auto s in list->GetRange(0, countX)) { sb->AppendFormat("x: {0}\n", s); } for each (auto s in list->GetRange(countX, countY)) { sb->AppendFormat("y: {0}\n", s); } for each (auto s in list->GetRange(countY, list->Count)) { sb->AppendLine(s); } return sb->ToString(); } }; struct device_info { std::wstring name; std::wstring id; }; std::vector get_unique_device_info() { std::vector 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^ rawInputHandles) { try { std::vector nativeHandles = rawinput_handles_from_dev_id( msclr::interop::marshal_as(deviceID)); for (auto nh : nativeHandles) rawInputHandles->Add(IntPtr(nh)); } catch (const std::exception& e) { throw gcnew InteropException(e); } } static List>^ GetDeviceIDs() { try { auto managed = gcnew List>(); for (auto&& [name, id] : get_unique_device_info()) { managed->Add( ValueTuple( msclr::interop::marshal_as(name), msclr::interop::marshal_as(id))); } return managed; } catch (const std::exception& e) { throw gcnew InteropException(e); } } }; public ref class ManagedAccel { ra::io_t* const instance = new ra::io_t(); public: ManagedAccel() {}; ManagedAccel(DriverSettings^ settings) { Settings = settings; } virtual ~ManagedAccel() { delete instance; } !ManagedAccel() { delete instance; } Tuple^ Accelerate(int x, int y, double time) { vec2d in_out_vec = { (double)x, (double)y }; instance->mod.modify(in_out_vec, time); return gcnew Tuple(in_out_vec.x, in_out_vec.y); } void Activate() { try { ra::write(*instance); } catch (const ra::error& e) { throw gcnew InteropException(e); } } property DriverSettings^ Settings { DriverSettings^ get() { DriverSettings^ settings = gcnew DriverSettings(); Marshal::PtrToStructure(IntPtr(&instance->args), settings); return settings; } void set(DriverSettings^ val) { Marshal::StructureToPtr(val, IntPtr(&instance->args), false); instance->mod = { instance->args }; } } static ManagedAccel^ GetActive() { try { auto active = gcnew ManagedAccel(); ra::read(*active->instance); return active; } catch (const ra::error& e) { throw gcnew InteropException(e); } } }; public ref struct VersionHelper { 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); } } };