summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authora1xd <[email protected]>2021-07-05 23:33:41 -0400
committera1xd <[email protected]>2021-07-05 23:33:41 -0400
commit31efc792f5895d7ef3533390875de3c480add996 (patch)
tree8db5b16a88f50448cb525ba8ae56801985294f63
parentMerge pull request #87 from matthewstrasiotto/streamer_mode (diff)
parentHandle power\exponent correctly in GUI (diff)
downloadrawaccel-31efc792f5895d7ef3533390875de3c480add996.tar.xz
rawaccel-31efc792f5895d7ef3533390875de3c480add996.zip
merge lut2
-rw-r--r--common/accel-base.hpp66
-rw-r--r--common/accel-classic.hpp127
-rw-r--r--common/accel-invoke.hpp52
-rw-r--r--common/accel-jump.hpp73
-rw-r--r--common/accel-linear.hpp31
-rw-r--r--common/accel-lookup.hpp306
-rw-r--r--common/accel-motivity.hpp117
-rw-r--r--common/accel-natural.hpp52
-rw-r--r--common/accel-naturalgain.hpp28
-rw-r--r--common/accel-noaccel.hpp6
-rw-r--r--common/accel-power.hpp23
-rw-r--r--common/accel-union.hpp102
-rw-r--r--common/common.vcxitems12
-rw-r--r--common/rawaccel-base.hpp109
-rw-r--r--common/rawaccel-error.hpp27
-rw-r--r--common/rawaccel-io-def.h4
-rw-r--r--common/rawaccel-io.hpp122
-rw-r--r--common/rawaccel-settings.h34
-rw-r--r--common/rawaccel-validate.hpp185
-rw-r--r--common/rawaccel-version.h34
-rw-r--r--common/rawaccel.hpp516
-rw-r--r--common/utility-rawinput.hpp2
-rw-r--r--common/utility.hpp88
-rw-r--r--common/x64-util.hpp30
-rw-r--r--converter/converter.cpp70
-rw-r--r--doc/Guide.md18
-rw-r--r--doc/images/LUT_example.pngbin0 -> 55969 bytes
-rw-r--r--doc/images/classic_example.pngbin52040 -> 54866 bytes
-rw-r--r--doc/images/jump_example.pngbin0 -> 47816 bytes
-rw-r--r--doc/images/linear_example.pngbin51428 -> 50547 bytes
-rw-r--r--doc/images/motivity_example.pngbin60657 -> 50916 bytes
-rw-r--r--doc/images/natural_gain_example.pngbin51433 -> 50679 bytes
-rw-r--r--doc/images/power_example.pngbin51330 -> 50340 bytes
-rw-r--r--driver/driver.cpp133
-rw-r--r--driver/driver.h9
-rw-r--r--driver/driver.vcxproj12
-rw-r--r--grapher/Constants/Constants.cs7
-rw-r--r--grapher/Form1.Designer.cs250
-rw-r--r--grapher/Form1.cs29
-rw-r--r--grapher/Layouts/ClassicLayout.cs5
-rw-r--r--grapher/Layouts/DefaultLayout.cs5
-rw-r--r--grapher/Layouts/JumpLayout.cs27
-rw-r--r--grapher/Layouts/LUTLayout.cs38
-rw-r--r--grapher/Layouts/LayoutBase.cs52
-rw-r--r--grapher/Layouts/LinearLayout.cs11
-rw-r--r--grapher/Layouts/MotivityLayout.cs7
-rw-r--r--grapher/Layouts/NaturalGainLayout.cs24
-rw-r--r--grapher/Layouts/NaturalLayout.cs7
-rw-r--r--grapher/Layouts/OffLayout.cs5
-rw-r--r--grapher/Layouts/PowerLayout.cs9
-rw-r--r--grapher/Layouts/UnsupportedLayout.cs32
-rw-r--r--grapher/Models/AccelGUI.cs147
-rw-r--r--grapher/Models/AccelGUIFactory.cs73
-rw-r--r--grapher/Models/Calculations/AccelCalculator.cs170
-rw-r--r--grapher/Models/Mouse/MouseWatcher.cs4
-rw-r--r--grapher/Models/Options/AccelOptionSet.cs12
-rw-r--r--grapher/Models/Options/AccelTypeOptions.cs265
-rw-r--r--grapher/Models/Options/ApplyOptions.cs61
-rw-r--r--grapher/Models/Options/CapOptions.cs225
-rw-r--r--grapher/Models/Options/CheckBoxOption.cs109
-rw-r--r--grapher/Models/Options/LUT/LUTPanelOptions.cs217
-rw-r--r--grapher/Models/Options/LUT/LUTPointOption.cs39
-rw-r--r--grapher/Models/Options/LUT/LutApplyOptions.cs183
-rw-r--r--grapher/Models/Options/OffsetOptions.cs5
-rw-r--r--grapher/Models/Options/TextOption.cs129
-rw-r--r--grapher/Models/Serialized/GUISettings.cs27
-rw-r--r--grapher/Models/Serialized/RawAccelSettings.cs141
-rw-r--r--grapher/Models/Serialized/SettingsManager.cs139
-rw-r--r--grapher/Properties/AssemblyInfo.cs4
-rw-r--r--grapher/grapher.csproj10
-rw-r--r--signed/driver/rawaccel.sysbin69896 -> 72304 bytes
-rw-r--r--wrapper/input.cpp74
-rw-r--r--wrapper/interop-exception.h21
-rw-r--r--wrapper/wrapper.cpp740
-rw-r--r--wrapper/wrapper.vcxproj7
-rw-r--r--wrapper/wrapper.vcxproj.filters8
-rw-r--r--wrapper/wrapper_io.cpp40
-rw-r--r--wrapper/wrapper_io.hpp25
-rw-r--r--writer/Program.cs106
-rw-r--r--writer/Properties/AssemblyInfo.cs4
80 files changed, 3759 insertions, 2122 deletions
diff --git a/common/accel-base.hpp b/common/accel-base.hpp
deleted file mode 100644
index 42b3bb1..0000000
--- a/common/accel-base.hpp
+++ /dev/null
@@ -1,66 +0,0 @@
-#pragma once
-
-namespace rawaccel {
-
- /// <summary> Struct to hold arguments for an acceleration function. </summary>
- struct accel_args {
- double offset = 0;
- bool legacy_offset = false;
- double accel = 0;
- double scale = 1;
- double limit = 2;
- double exponent = 2;
- double midpoint = 10;
- double weight = 1;
- double scale_cap = 0;
- double gain_cap = 0;
- double speed_cap = 0;
- };
-
- struct domain_args {
- vec2d domain_weights = { 1, 1 };
- double lp_norm = 2;
- };
-
- template <typename Func>
- struct accel_val_base {
- bool legacy_offset = false;
- double offset = 0;
- double weight = 1;
- Func fn;
-
- accel_val_base(const accel_args& args) : fn(args) {}
-
- };
-
- template <typename Func>
- struct additive_accel : accel_val_base<Func> {
-
- additive_accel(const accel_args& args) : accel_val_base(args) {
- this->legacy_offset = args.legacy_offset;
- this->offset = args.offset;
- this->weight = args.weight;
- }
-
- inline double operator()(double speed) const {
- double offset_speed = speed - this->offset;
- if (offset_speed <= 0) return 1;
- if (this->legacy_offset) return 1 + this->fn.legacy_offset(offset_speed) * this->weight;
- return 1 + this->fn(offset_speed) * this->weight;
- }
- };
-
- template <typename Func>
- struct nonadditive_accel : accel_val_base<Func> {
-
- nonadditive_accel(const accel_args& args) : accel_val_base(args) {
- if (args.weight > 0) this->weight = args.weight;
- }
-
- inline double operator()(double speed) const {
- return this->fn(speed) * this->weight;
- }
-
- };
-
-}
diff --git a/common/accel-classic.hpp b/common/accel-classic.hpp
index 1df888a..4385897 100644
--- a/common/accel-classic.hpp
+++ b/common/accel-classic.hpp
@@ -1,36 +1,105 @@
#pragma once
-#include <math.h>
+#include "rawaccel-base.hpp"
+#include "utility.hpp"
-#include "accel-base.hpp"
+#include <math.h>
+#include <float.h>
namespace rawaccel {
- /// <summary> Struct to hold "classic" (linear raised to power) acceleration implementation. </summary>
- struct classic_impl {
- double accel;
- double power;
- double power_inc;
- double offset;
- double multiplicative_const;
-
- classic_impl(const accel_args& args) :
- accel(args.accel), power(args.exponent - 1), offset(args.offset) {
- multiplicative_const = pow(accel, power);
- power_inc = power + 1;
- }
-
- inline double operator()(double speed) const {
- //f(x) = (mx)^(k-1)
- double base_speed = speed + offset;
- return multiplicative_const * pow(speed, power_inc) / base_speed;
- }
-
- inline double legacy_offset(double speed) const {
- return pow(accel * speed, power);
- }
- };
-
- using accel_classic = additive_accel<classic_impl>;
-
+ /// <summary> Struct to hold "classic" (linear raised to power) acceleration implementation. </summary>
+ struct classic_base {
+ double offset;
+ double power;
+ double accel_raised;
+
+ classic_base(const accel_args& args) :
+ offset(args.offset),
+ power(args.power),
+ accel_raised(pow(args.accel_classic, power - 1)) {}
+
+ double base_fn(double x) const
+ {
+ return accel_raised * pow(x - offset, power) / x;
+ }
+ };
+
+ struct classic_legacy : classic_base {
+ double sens_cap = DBL_MAX;
+ double sign = 1;
+
+ classic_legacy(const accel_args& args) :
+ classic_base(args)
+ {
+ if (args.cap > 0) {
+ sens_cap = args.cap - 1;
+
+ if (sens_cap < 0) {
+ sens_cap = -sens_cap;
+ sign = -sign;
+ }
+ }
+ }
+
+ double operator()(double x) const
+ {
+ if (x <= offset) return 1;
+ return sign * minsd(base_fn(x), sens_cap) + 1;
+ }
+ };
+
+ struct classic : classic_base {
+ vec2d gain_cap = { DBL_MAX, DBL_MAX };
+ double constant = 0;
+ double sign = 1;
+
+ classic(const accel_args& args) :
+ classic_base(args)
+ {
+ if (args.cap > 0) {
+ gain_cap.y = args.cap - 1;
+
+ if (gain_cap.y < 0) {
+ gain_cap.y = -gain_cap.y;
+ sign = -sign;
+ }
+
+ gain_cap.x = gain_inverse(gain_cap.y, args.accel_classic, power, offset);
+ constant = (base_fn(gain_cap.x) - gain_cap.y) * gain_cap.x;
+ }
+ }
+
+ double operator()(double x) const
+ {
+ double output;
+
+ if (x <= offset) return 1;
+
+ if (x < gain_cap.x) {
+ output = base_fn(x);
+ }
+ else {
+ output = constant / x + gain_cap.y;
+ }
+
+ return sign * output + 1;
+ }
+
+ static double gain(double x, double accel, double power, double offset)
+ {
+ return power * pow(accel * (x - offset), power - 1);
+ }
+
+ static double gain_inverse(double y, double accel, double power, double offset)
+ {
+ return (accel * offset + pow(y / power, 1 / (power - 1))) / accel;
+ }
+
+ static double gain_accel(double x, double y, double power, double offset)
+ {
+ return -pow(y / power, 1 / (power - 1)) / (offset - x);
+ }
+ };
+
}
diff --git a/common/accel-invoke.hpp b/common/accel-invoke.hpp
new file mode 100644
index 0000000..f2a95dc
--- /dev/null
+++ b/common/accel-invoke.hpp
@@ -0,0 +1,52 @@
+#pragma once
+
+#include "accel-union.hpp"
+
+namespace rawaccel {
+
+ class accel_invoker {
+ using callback_t = double (*)(const accel_union&, double, double);
+
+ callback_t cb = &invoke_impl<accel_noaccel>;
+
+ template <typename T>
+ static double invoke_impl(const accel_union& u, double x, double w)
+ {
+ return apply_weighted(reinterpret_cast<const T&>(u), x, w);
+ }
+
+ public:
+
+ accel_invoker(const accel_args& args)
+ {
+ cb = visit_accel([](auto&& arg) {
+ using T = remove_ref_t<decltype(arg)>;
+
+ if constexpr (is_same_v<T, motivity>) {
+ static_assert(sizeof motivity == sizeof binlog_lut);
+ return &invoke_impl<binlog_lut>;
+ }
+ else {
+ return &invoke_impl<T>;
+ }
+
+ }, make_mode(args), accel_union{});
+ }
+
+ accel_invoker() = default;
+
+ double invoke(const accel_union& u, double x, double weight = 1) const
+ {
+ return (*cb)(u, x, weight);
+ }
+ };
+
+ inline vec2<accel_invoker> invokers(const settings& args)
+ {
+ return {
+ accel_invoker(args.argsv.x),
+ accel_invoker(args.argsv.y)
+ };
+ }
+
+}
diff --git a/common/accel-jump.hpp b/common/accel-jump.hpp
new file mode 100644
index 0000000..198891a
--- /dev/null
+++ b/common/accel-jump.hpp
@@ -0,0 +1,73 @@
+#pragma once
+
+#include "rawaccel-base.hpp"
+
+namespace rawaccel {
+
+ struct jump_base {
+ vec2d step;
+ double smooth_rate;
+
+ jump_base(const accel_args& args) :
+ step({ args.offset, args.cap - 1 })
+ {
+ if (args.smooth == 0 || args.offset == 0) {
+ smooth_rate = 0;
+ }
+ else {
+ smooth_rate = 2 * PI / (args.offset * args.smooth);
+ }
+
+ }
+
+ bool is_smooth() const
+ {
+ return smooth_rate != 0;
+ }
+
+ double decay(double x) const
+ {
+ return exp(smooth_rate * (step.x - x));
+ }
+
+ double smooth(double x) const
+ {
+ return step.y / (1 + decay(x));
+ }
+
+ double smooth_antideriv(double x) const
+ {
+ return step.y * (x + log(1 + decay(x)) / smooth_rate);
+ }
+ };
+
+ struct jump_legacy : jump_base {
+ using jump_base::jump_base;
+
+ double operator()(double x) const
+ {
+ if (is_smooth()) return smooth(x) + 1;
+ else if (x < step.x) return 1;
+ else return step.y;
+ }
+ };
+
+ struct jump : jump_base {
+ double C;
+
+ jump(const accel_args& args) :
+ jump_base(args),
+ C(-smooth_antideriv(0)) {}
+
+ double operator()(double x) const
+ {
+ if (x <= 0) return 1;
+
+ if (is_smooth()) return 1 + (smooth_antideriv(x) + C) / x;
+
+ if (x < step.x) return 1;
+ else return 1 + step.y * (x - step.x) / x;
+ }
+ };
+
+}
diff --git a/common/accel-linear.hpp b/common/accel-linear.hpp
deleted file mode 100644
index 2bd57b8..0000000
--- a/common/accel-linear.hpp
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma once
-
-#include "accel-base.hpp"
-
-namespace rawaccel {
-
- /// <summary> Struct to hold linear acceleration implementation. </summary>
- struct linear_impl {
- double accel;
- double offset;
- double subtractive_const;
- double divisive_const;
-
- linear_impl(const accel_args& args) : accel(args.accel), offset(args.offset) {
- subtractive_const = 2 * accel * offset;
- divisive_const = accel * offset * offset;
- }
-
- inline double operator()(double speed) const {
- double base_speed = speed + offset;
- return accel * base_speed - subtractive_const + divisive_const / base_speed;
- }
-
- inline double legacy_offset(double speed) const {
- return accel * speed;
- }
- };
-
- using accel_linear = additive_accel<linear_impl>;
-
-}
diff --git a/common/accel-lookup.hpp b/common/accel-lookup.hpp
new file mode 100644
index 0000000..99f39e9
--- /dev/null
+++ b/common/accel-lookup.hpp
@@ -0,0 +1,306 @@
+#pragma once
+
+#include "rawaccel-base.hpp"
+#include "utility.hpp"
+
+#include <math.h>
+
+namespace rawaccel {
+
+ struct linear_range {
+ double start;
+ double stop;
+ int num;
+
+ template <typename Func>
+ void for_each(Func fn) const
+ {
+ double interval = (stop - start) / (num - 1);
+ for (int i = 0; i < num; i++) {
+ fn(i * interval + start);
+ }
+ }
+
+ int size() const
+ {
+ return num;
+ }
+ };
+
+
+ // represents the range [2^start, 2^stop], with num - 1
+ // elements linearly spaced between each exponential step
+ struct fp_rep_range {
+ int start;
+ int stop;
+ int num;
+
+ template <typename Func>
+ void for_each(Func fn) const
+ {
+ for (int e = 0; e < stop - start; e++) {
+ double exp_scale = scalbn(1, e + start) / num;
+
+ for (int i = 0; i < num; i++) {
+ fn((i + num) * exp_scale);
+ }
+ }
+
+ fn(scalbn(1, stop));
+ }
+
+ int size() const
+ {
+ return (stop - start) * num + 1;
+ }
+ };
+
+ template <typename Lookup>
+ struct lut_base {
+ enum { capacity = SPACED_LUT_CAPACITY };
+ using value_t = float;
+
+ template <typename Func>
+ void fill(Func fn)
+ {
+ auto* self = static_cast<Lookup*>(this);
+
+ self->range.for_each([&, fn, i = 0](double x) mutable {
+ self->data[i++] = static_cast<value_t>(fn(x));
+ });
+ }
+
+ };
+
+ struct linear_lut : lut_base<linear_lut> {
+ linear_range range;
+ bool transfer = false;
+ value_t data[capacity] = {};
+
+ double operator()(double x) const
+ {
+ if (x > range.start) {
+ double range_dist = range.stop - range.start;
+ double idx_f = (x - range.start) * (range.num - 1) / range_dist;
+
+ unsigned idx = min(static_cast<int>(idx_f), range.size() - 2);
+
+ if (idx < capacity - 1) {
+ double y = lerp(data[idx], data[idx + 1], idx_f - idx);
+ if (transfer) y /= x;
+ return y;
+ }
+ }
+
+ double y = data[0];
+ if (transfer) y /= range.start;
+ return y;
+ }
+
+ linear_lut(const spaced_lut_args& args) :
+ range({
+ args.start,
+ args.stop,
+ args.num_elements
+ }),
+ transfer(args.transfer) {}
+
+ linear_lut(const accel_args& args) :
+ linear_lut(args.spaced_args) {}
+ };
+
+ struct binlog_lut : lut_base<binlog_lut> {
+ fp_rep_range range;
+ double x_start;
+ bool transfer = false;
+ value_t data[capacity] = {};
+
+ double operator()(double x) const
+ {
+ int e = min(ilogb(x), range.stop - 1);
+
+ if (e >= range.start) {
+ int idx_int_log_part = e - range.start;
+ double idx_frac_lin_part = scalbn(x, -e) - 1;
+ double idx_f = range.num * (idx_int_log_part + idx_frac_lin_part);
+
+ unsigned idx = min(static_cast<int>(idx_f), range.size() - 2);
+
+ if (idx < capacity - 1) {
+ double y = lerp(data[idx], data[idx + 1], idx_f - idx);
+ if (transfer) y /= x;
+ return y;
+ }
+ }
+
+ double y = data[0];
+ if (transfer) y /= x_start;
+ return y;
+ }
+
+ binlog_lut(const spaced_lut_args& args) :
+ range({
+ static_cast<int>(args.start),
+ static_cast<int>(args.stop),
+ args.num_elements
+ }),
+ x_start(scalbn(1, range.start)),
+ transfer(args.transfer) {}
+
+ binlog_lut(const accel_args& args) :
+ binlog_lut(args.spaced_args) {}
+ };
+
+ struct si_pair {
+ float slope = 0;
+ float intercept = 0;
+ };
+
+ struct arbitrary_lut_point {
+ float applicable_speed = 0;
+ si_pair slope_intercept = {};
+ };
+
+ struct arbitrary_lut {
+ enum { capacity = ARB_LUT_CAPACITY };
+
+ fp_rep_range range;
+ arbitrary_lut_point data[capacity] = {};
+ int log_lookup[capacity] = {};
+ double first_point_speed;
+ double last_point_speed;
+ int last_arbitrary_index;
+ int last_log_lookup_index;
+ double last_log_lookup_speed;
+ double first_log_lookup_speed;
+ bool velocity_points;
+
+ double operator()(double speed) const
+ {
+ int index = 0;
+ int last_arb_index = last_arbitrary_index;
+ int last_log_index = last_log_lookup_index;
+
+ if (speed <= 0) return 1;
+
+ if (unsigned(last_arb_index) < capacity &&
+ unsigned(last_log_index) < capacity &&
+ speed > first_point_speed)
+ {
+ if (speed > last_point_speed)
+ {
+ index = last_arb_index;
+ }
+ else if (speed > last_log_lookup_speed)
+ {
+ int last_log = log_lookup[last_log_index];
+ if (unsigned(last_log) >= capacity) return 1;
+ index = search_from(last_log, last_arb_index, speed);
+ }
+ else if (speed < first_log_lookup_speed)
+ {
+ index = search_from(0, last_arb_index, speed);
+ }
+ else
+ {
+ int log_index = get_log_index(speed);
+ if (unsigned(log_index) >= capacity) return 1;
+ int arbitrary_index = log_lookup[log_index];
+ if (arbitrary_index < 0) return 1;
+ index = search_from(arbitrary_index, last_arb_index, speed);
+ }
+
+ }
+
+ return apply(index, speed);
+ }
+
+ int inline get_log_index(double speed) const
+ {
+ double speed_log = log(speed) - range.start;
+ int index = (int)floor(speed_log * range.num);
+ return index;
+ }
+
+ int inline search_from(int index, int last, double speed) const
+ {
+ do
+ {
+ index++;
+ }
+ while (index <= last && data[index].applicable_speed < speed);
+
+ return index - 1;
+ }
+
+ double inline apply(int index, double speed) const
+ {
+ auto [slope, intercept] = data[index].slope_intercept;
+
+ if (velocity_points)
+ {
+ return slope + intercept / speed;
+ }
+ else
+ {
+ return slope * speed + intercept;
+ }
+ }
+
+ void fill(const vec2<float>* points, int length)
+ {
+ first_point_speed = points[0].x;
+ last_arbitrary_index = length - 1;
+ // -2 because the last index in the arbitrary array is used for slope-intercept only
+ last_point_speed = points[length-2].x;
+
+ int start = static_cast<int>(floor(log(first_point_speed)));
+ first_log_lookup_speed = exp(start*1.0);
+ int end = static_cast<int>(floor(log(last_point_speed)));
+ last_log_lookup_speed = exp(end*1.0);
+ int num = end > start ? static_cast<int>(capacity / (end - start)) : 1;
+ range = fp_rep_range{ start, end, num };
+ last_log_lookup_index = end > start ? num * (end - start) - 1 : 0;
+
+ vec2<float> current = {0, velocity_points ? 0.0f : 1.0f };
+ vec2<float> next;
+ int log_index = 0;
+ double log_inner_iterator = range.start;
+ double log_inner_slice = 1.0 / (range.num * 1.0);
+ double log_value = exp(log_inner_iterator);
+
+ for (int i = 0; i < length; i++)
+ {
+ next = points[i];
+ double slope = (next.y - current.y) / (next.x - current.x);
+ double intercept = next.y - slope * next.x;
+ si_pair current_si = {
+ static_cast<float>(slope),
+ static_cast<float>(intercept)
+ };
+ arbitrary_lut_point current_lut_point = {
+ static_cast<float>(current.x),
+ current_si
+ };
+
+ this->data[i] = current_lut_point;
+
+ while (log_value < next.x && log_inner_iterator < end)
+ {
+ this->log_lookup[log_index] = i;
+ log_index++;
+ log_inner_iterator += log_inner_slice;
+ log_value = exp(log_inner_iterator);
+ }
+
+ current = next;
+ }
+ }
+
+ arbitrary_lut(const accel_args& args)
+ {
+ velocity_points = args.arb_args.velocity;
+ fill(args.arb_args.data, args.arb_args.length);
+ }
+ };
+}
diff --git a/common/accel-motivity.hpp b/common/accel-motivity.hpp
index 246cf37..0efe7ea 100644
--- a/common/accel-motivity.hpp
+++ b/common/accel-motivity.hpp
@@ -1,101 +1,56 @@
#pragma once
-#include <math.h>
-
-#include "accel-base.hpp"
+#include "accel-lookup.hpp"
-#define RA_LOOKUP
+#include <math.h>
namespace rawaccel {
- constexpr size_t LUT_SIZE = 601;
-
- struct si_pair {
- double slope = 0;
- double intercept = 0;
- };
-
- /// <summary> Struct to hold sigmoid (s-shaped) gain implementation. </summary>
- struct motivity_impl {
- double rate;
- double limit;
+ struct sigmoid {
+ double accel;
+ double motivity;
double midpoint;
- double subtractive_constant;
+ double constant;
- motivity_impl(const accel_args& args) :
- rate(pow(10,args.accel)), limit(2*log10(args.limit)), midpoint(log10(args.midpoint))
- {
- subtractive_constant = limit / 2;
- }
+ sigmoid(const accel_args& args) :
+ accel(exp(args.growth_rate)),
+ motivity(2 * log(args.motivity)),
+ midpoint(log(args.midpoint)),
+ constant(-motivity / 2) {}
- inline double operator()(double speed) const {
- double log_speed = log10(speed);
- return pow(10, limit / (exp(-rate * (log_speed - midpoint)) + 1) - subtractive_constant);
-
- }
-
- inline double legacy_offset(double speed) const { return operator()(speed); }
-
- inline double apply(si_pair* lookup, double speed) const
+ double operator()(double x) const
{
- si_pair pair = lookup[map(speed)];
- return pair.slope + pair.intercept / speed;
+ double denom = exp(accel * (midpoint - log(x))) + 1;
+ return exp(motivity / denom + constant);
}
+ };
- inline int map(double speed) const
- {
- int index = speed > 0 ? (int)(100 * log10(speed) + 201) : 0;
-
- if (index < 0) return 0;
- if (index >= LUT_SIZE) return LUT_SIZE - 1;
+ /// <summary> Struct to hold sigmoid (s-shaped) gain implementation. </summary>
+ struct motivity : binlog_lut {
- return index;
- }
+ using binlog_lut::operator();
- inline void fill(si_pair* lookup) const
+ motivity(const accel_args& args) :
+ binlog_lut(args)
{
- double lookup_speed = 0;
- double integral_interval = 0;
- double gain_integral_speed = 0;
- double gain_integral_speed_prev = 0;
- double gain = 0;
- double intercept = 0;
- double output = 0;
- double output_prev = 0;
- double x = -2;
-
- double logarithm_interval = 0.01;
- double integral_intervals_per_speed = 10;
- double integral_interval_factor = pow(10, logarithm_interval) / integral_intervals_per_speed;
-
- lookup[0] = {};
-
- for (size_t i = 1; i < LUT_SIZE; i++)
- {
- x += logarithm_interval;
-
- // Each lookup speed will be 10^0.01 = 2.33% higher than the previous
- // To get 10 integral intervals per speed, set interval to 0.233%
- lookup_speed = pow(10, x);
- integral_interval = lookup_speed * integral_interval_factor;
-
- while (gain_integral_speed < lookup_speed)
- {
- output_prev = output;
- gain_integral_speed_prev = gain_integral_speed;
- gain_integral_speed += integral_interval;
- gain = operator()(gain_integral_speed);
- output += gain * integral_interval;
+ double sum = 0;
+ double a = 0;
+ auto sigmoid_sum = [&, sig = sigmoid(args)](double b) mutable {
+ double interval = (b - a) / args.spaced_args.partitions;
+ for (int i = 1; i <= args.spaced_args.partitions; i++) {
+ sum += sig(a + i * interval) * interval;
}
-
- intercept = output_prev - gain_integral_speed_prev * gain;
-
- lookup[i] = { gain, intercept };
- }
-
+ a = b;
+ return sum;
+ };
+
+ fill([&](double x) {
+ double y = sigmoid_sum(x);
+ if (!this->transfer) y /= x;
+ return y;
+ });
}
- };
- using accel_motivity = nonadditive_accel<motivity_impl>;
+ };
}
diff --git a/common/accel-natural.hpp b/common/accel-natural.hpp
index 03700c1..9f76d1a 100644
--- a/common/accel-natural.hpp
+++ b/common/accel-natural.hpp
@@ -1,35 +1,55 @@
#pragma once
-#include <math.h>
+#include "rawaccel-base.hpp"
-#include "accel-base.hpp"
+#include <math.h>
namespace rawaccel {
/// <summary> Struct to hold "natural" (vanishing difference) acceleration implementation. </summary>
- struct natural_impl {
- double rate;
- double limit;
+ struct natural_base {
double offset;
+ double accel;
+ double limit;
- natural_impl(const accel_args& args) :
- rate(args.accel), limit(args.limit - 1), offset(args.offset)
+ natural_base(const accel_args& args) :
+ offset(args.offset),
+ limit(args.limit - 1)
{
- rate /= limit;
+ accel = args.decay_rate / fabs(limit);
}
+ };
- inline double operator()(double speed) const {
- // f(x) = k(1-e^(-mx))
- double base_speed = speed + offset;
- return limit * (1 - ((exp(-rate * speed) * speed + offset) / base_speed));
- }
+ struct natural_legacy : natural_base {
+
+ double operator()(double x) const
+ {
+ if (x <= offset) return 1;
- inline double legacy_offset(double speed) const {
- return limit - (limit * exp(-rate * speed));
+ double offset_x = offset - x;
+ double decay = exp(accel * offset_x);
+ return limit * (1 - (offset - decay * offset_x) / x) + 1;
}
+ using natural_base::natural_base;
};
- using accel_natural = additive_accel<natural_impl>;
+ struct natural : natural_base {
+ double constant;
+
+ double operator()(double x) const
+ {
+ if (x <= offset) return 1;
+
+ double offset_x = offset - x;
+ double decay = exp(accel * offset_x);
+ double output = limit * (decay / accel - offset_x) + constant;
+ return output / x + 1;
+ }
+
+ natural(const accel_args& args) :
+ natural_base(args),
+ constant(-limit / accel) {}
+ };
}
diff --git a/common/accel-naturalgain.hpp b/common/accel-naturalgain.hpp
deleted file mode 100644
index cdfd1fa..0000000
--- a/common/accel-naturalgain.hpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-
-#include <math.h>
-
-#include "accel-natural.hpp"
-
-namespace rawaccel {
-
- /// <summary> Struct to hold "natural" (vanishing difference) gain implementation. </summary>
- struct naturalgain_impl : natural_impl {
-
- using natural_impl::natural_impl;
-
- inline double operator()(double speed) const {
- // f(x) = k((e^(-mx)-1)/mx + 1)
- double base_speed = speed + offset;
- double scaled_speed = rate * speed;
- return limit * (((exp(-scaled_speed) - 1) / (base_speed * rate) ) + 1 - offset / base_speed);
- }
-
- inline double legacy_offset(double speed) const {
- double scaled_speed = rate * speed;
- return limit * (((exp(-scaled_speed) - 1) / scaled_speed) + 1);
- }
- };
-
- using accel_naturalgain = additive_accel<naturalgain_impl>;
-}
diff --git a/common/accel-noaccel.hpp b/common/accel-noaccel.hpp
index c803c2f..8d1e758 100644
--- a/common/accel-noaccel.hpp
+++ b/common/accel-noaccel.hpp
@@ -1,6 +1,6 @@
#pragma once
-#include "accel-base.hpp"
+#include "rawaccel-base.hpp"
namespace rawaccel {
@@ -10,9 +10,7 @@ namespace rawaccel {
accel_noaccel(const accel_args&) {}
accel_noaccel() = default;
- inline double operator()(double) const { return 1; }
-
- inline double legacy_offset(double speed) const { return operator()(speed); }
+ double operator()(double) const { return 1; }
};
}
diff --git a/common/accel-power.hpp b/common/accel-power.hpp
index 5d0c451..c8faabb 100644
--- a/common/accel-power.hpp
+++ b/common/accel-power.hpp
@@ -1,26 +1,27 @@
#pragma once
-#include <math.h>
+#include "rawaccel-base.hpp"
-#include "accel-base.hpp"
+#include <math.h>
namespace rawaccel {
/// <summary> Struct to hold power (non-additive) acceleration implementation. </summary>
- struct power_impl {
- double scale;
+ struct power {
+ double pre_scale;
double exponent;
+ double post_scale;
- power_impl(const accel_args& args) :
- scale(args.scale), exponent(args.exponent)
- {}
+ power(const accel_args& args) :
+ pre_scale(args.scale),
+ exponent(args.exponent),
+ post_scale(args.weight) {}
- inline double operator()(double speed) const {
+ double operator()(double speed) const
+ {
// f(x) = (mx)^k
- return pow(speed * scale, exponent);
+ return post_scale * pow(speed * pre_scale, exponent);
}
};
- using accel_power = nonadditive_accel<power_impl>;
-
}
diff --git a/common/accel-union.hpp b/common/accel-union.hpp
new file mode 100644
index 0000000..8495a62
--- /dev/null
+++ b/common/accel-union.hpp
@@ -0,0 +1,102 @@
+#pragma once
+
+#include "accel-classic.hpp"
+#include "accel-jump.hpp"
+#include "accel-natural.hpp"
+#include "accel-power.hpp"
+#include "accel-motivity.hpp"
+#include "accel-noaccel.hpp"
+
+namespace rawaccel {
+
+ enum class internal_mode {
+ classic_lgcy,
+ classic_gain,
+ jump_lgcy,
+ jump_gain,
+ natural_lgcy,
+ natural_gain,
+ motivity_lgcy,
+ motivity_gain,
+ power,
+ lut_arb,
+ lut_log,
+ lut_lin,
+ noaccel
+ };
+
+ constexpr internal_mode make_mode(accel_mode mode, spaced_lut_mode lut_mode, bool legacy)
+ {
+ if (lut_mode != spaced_lut_mode::off) {
+ switch (lut_mode) {
+ case spaced_lut_mode::binlog: return internal_mode::lut_log;
+ case spaced_lut_mode::linear: return internal_mode::lut_lin;
+ default: return internal_mode::noaccel;
+ }
+ }
+ else if (mode == accel_mode::power) {
+ return internal_mode::power;
+ }
+ else if (mode == accel_mode::arb_lookup) {
+ return internal_mode::lut_arb;
+ }
+ else if (mode >= accel_mode::noaccel) {
+ return internal_mode::noaccel;
+ }
+ else {
+ int im = static_cast<int>(mode) * 2 + (legacy ? 0 : 1);
+ return static_cast<internal_mode>(im);
+ }
+ }
+
+ constexpr internal_mode make_mode(const accel_args& args)
+ {
+ return make_mode(args.mode, args.spaced_args.mode, args.legacy);
+ }
+
+ template <typename Visitor, typename AccelUnion>
+ constexpr auto visit_accel(Visitor vis, internal_mode mode, AccelUnion&& u)
+ {
+ switch (mode) {
+ case internal_mode::classic_lgcy: return vis(u.classic_l);
+ case internal_mode::classic_gain: return vis(u.classic_g);
+ case internal_mode::jump_lgcy: return vis(u.jump_l);
+ case internal_mode::jump_gain: return vis(u.jump_g);
+ case internal_mode::natural_lgcy: return vis(u.natural_l);
+ case internal_mode::natural_gain: return vis(u.natural_g);
+ case internal_mode::motivity_lgcy: return vis(u.motivity_l);
+ case internal_mode::motivity_gain: return vis(u.motivity_g);
+ case internal_mode::power: return vis(u.power);
+ case internal_mode::lut_arb: return vis(u.arb_lut);
+ case internal_mode::lut_log: return vis(u.log_lut);
+ case internal_mode::lut_lin: return vis(u.lin_lut);
+ default: return vis(u.noaccel);
+ }
+ }
+
+ union accel_union {
+ classic classic_g;
+ classic_legacy classic_l;
+ jump jump_g;
+ jump_legacy jump_l;
+ natural natural_g;
+ natural_legacy natural_l;
+ power power;
+ sigmoid motivity_l;
+ motivity motivity_g;
+ linear_lut lin_lut;
+ binlog_lut log_lut;
+ arbitrary_lut arb_lut;
+ accel_noaccel noaccel = {};
+
+ accel_union(const accel_args& args)
+ {
+ visit_accel([&](auto& impl) {
+ impl = { args };
+ }, make_mode(args), *this);
+ }
+
+ accel_union() = default;
+ };
+
+}
diff --git a/common/common.vcxitems b/common/common.vcxitems
index ba9bd98..2cf2df2 100644
--- a/common/common.vcxitems
+++ b/common/common.vcxitems
@@ -14,23 +14,25 @@
<ProjectCapability Include="SourceItemsFromImports" />
</ItemGroup>
<ItemGroup>
- <ClInclude Include="$(MSBuildThisFileDirectory)accel-base.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)accel-classic.hpp" />
+ <ClInclude Include="$(MSBuildThisFileDirectory)accel-invoke.hpp" />
+ <ClInclude Include="$(MSBuildThisFileDirectory)accel-jump.hpp" />
+ <ClInclude Include="$(MSBuildThisFileDirectory)accel-lookup.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)accel-motivity.hpp" />
- <ClInclude Include="$(MSBuildThisFileDirectory)accel-linear.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)accel-natural.hpp" />
- <ClInclude Include="$(MSBuildThisFileDirectory)accel-naturalgain.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)accel-noaccel.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)accel-power.hpp" />
+ <ClInclude Include="$(MSBuildThisFileDirectory)accel-union.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)rawaccel-error.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)rawaccel-io-def.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)rawaccel-io.hpp" />
- <ClInclude Include="$(MSBuildThisFileDirectory)rawaccel-settings.h" />
+ <ClInclude Include="$(MSBuildThisFileDirectory)rawaccel-base.hpp" />
+ <ClInclude Include="$(MSBuildThisFileDirectory)rawaccel-validate.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)rawaccel-version.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)rawaccel.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)utility-install.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)utility-rawinput.hpp" />
- <ClInclude Include="$(MSBuildThisFileDirectory)x64-util.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)vec2.h" />
+ <ClInclude Include="$(MSBuildThisFileDirectory)utility.hpp" />
</ItemGroup>
</Project> \ No newline at end of file
diff --git a/common/rawaccel-base.hpp b/common/rawaccel-base.hpp
new file mode 100644
index 0000000..c1b2db3
--- /dev/null
+++ b/common/rawaccel-base.hpp
@@ -0,0 +1,109 @@
+#pragma once
+
+#include "vec2.h"
+
+namespace rawaccel {
+ using milliseconds = double;
+
+ inline constexpr int POLL_RATE_MIN = 125;
+ inline constexpr int POLL_RATE_MAX = 8000;
+
+ inline constexpr milliseconds DEFAULT_TIME_MIN = 1000.0 / POLL_RATE_MAX / 2;
+ inline constexpr milliseconds DEFAULT_TIME_MAX = 1000.0 / POLL_RATE_MIN * 2;
+
+ inline constexpr milliseconds WRITE_DELAY = 1000;
+
+ inline constexpr size_t MAX_DEV_ID_LEN = 200;
+
+ inline constexpr size_t SPACED_LUT_CAPACITY = 1025;
+ inline constexpr size_t ARB_LUT_CAPACITY = SPACED_LUT_CAPACITY / 4;
+
+ inline constexpr double MAX_NORM = 16;
+ inline constexpr double PI = 3.14159265358979323846;
+
+ enum class accel_mode {
+ classic,
+ jump,
+ natural,
+ motivity,
+ power,
+ arb_lookup,
+ noaccel
+ };
+
+ enum class spaced_lut_mode {
+ off,
+ binlog,
+ linear
+ };
+
+ struct spaced_lut_args {
+ spaced_lut_mode mode = spaced_lut_mode::off;
+ bool transfer = true;
+ unsigned char partitions = 2;
+ short num_elements = 8;
+ double start = 0;
+ double stop = 8;
+ };
+
+ struct table_args {
+ bool velocity = true;
+ int length = 0;
+ vec2<float> data[ARB_LUT_CAPACITY] = {};
+ };
+
+ struct accel_args {
+ accel_mode mode = accel_mode::noaccel;
+ bool legacy = false;
+
+ double offset = 0;
+ double cap = 1.5;
+ double accel_classic = 0.005;
+ double decay_rate = 0.1;
+ double growth_rate = 1;
+ double motivity = 1.5;
+ double power = 2;
+ double scale = 1;
+ double weight = 1;
+ double exponent = 0.05;
+ double limit = 1.5;
+ double midpoint = 5;
+ double smooth = 0.5;
+
+ spaced_lut_args spaced_args;
+ table_args arb_args;
+ };
+
+ struct domain_args {
+ vec2d domain_weights = { 1, 1 };
+ double lp_norm = 2;
+ };
+
+ struct settings {
+ double degrees_rotation = 0;
+ double degrees_snap = 0;
+ bool combine_mags = true;
+ double dpi = 1000;
+ double speed_min = 0;
+ double speed_max = 0;
+
+ vec2<accel_args> argsv;
+ vec2d sens = { 1, 1 };
+ vec2d dir_multipliers = { 1, 1 };
+ domain_args dom_args = {};
+ vec2d range_weights = { 1, 1 };
+
+ milliseconds time_min = DEFAULT_TIME_MIN;
+ milliseconds time_max = DEFAULT_TIME_MAX;
+
+ bool ignore = false;
+ wchar_t device_id[MAX_DEV_ID_LEN] = {};
+ };
+
+ template <typename AccelFunc>
+ inline double apply_weighted(AccelFunc&& f, double x, double w)
+ {
+ return 1 + (f(x) - 1) * w;
+ }
+
+}
diff --git a/common/rawaccel-error.hpp b/common/rawaccel-error.hpp
index cdbe1e5..a9cb7b8 100644
--- a/common/rawaccel-error.hpp
+++ b/common/rawaccel-error.hpp
@@ -1,11 +1,12 @@
#pragma once
-#include <stdexcept>
+#include <system_error>
+#include <string>
namespace rawaccel {
- class error : public std::runtime_error {
- using std::runtime_error::runtime_error;
+ class error : public std::runtime_error {
+ using std::runtime_error::runtime_error;
};
class io_error : public error {
@@ -14,7 +15,25 @@ namespace rawaccel {
class install_error : public io_error {
public:
- install_error() : io_error("Raw Accel driver is not installed, run installer.exe") {}
+ install_error() :
+ io_error("Raw Accel is not installed, run installer.exe") {}
+ };
+
+ class sys_error : public io_error {
+ public:
+ sys_error(const char* msg, DWORD code = GetLastError()) :
+ io_error(build_msg(code, msg)) {}
+
+ static std::string build_msg(DWORD code, const char* msg)
+ {
+ std::string ret =
+ std::system_error(code, std::system_category(), msg).what();
+ ret += " (";
+ ret += std::to_string(code);
+ ret += ")";
+ return ret;
+ }
+
};
}
diff --git a/common/rawaccel-io-def.h b/common/rawaccel-io-def.h
index e169390..399e0f2 100644
--- a/common/rawaccel-io-def.h
+++ b/common/rawaccel-io-def.h
@@ -1,9 +1,11 @@
#pragma once
+#define NOMINMAX
+
#ifdef _KERNEL_MODE
#include <ntddk.h>
#else
-#include <winioctl.h>
+#include <Windows.h>
#endif
#define RA_DEV_TYPE 0x8888u
diff --git a/common/rawaccel-io.hpp b/common/rawaccel-io.hpp
index 703ea92..a80e254 100644
--- a/common/rawaccel-io.hpp
+++ b/common/rawaccel-io.hpp
@@ -1,66 +1,84 @@
#pragma once
-#include <system_error>
-
-#define NOMINMAX
-#include <Windows.h>
-
#include "rawaccel-io-def.h"
-#include "rawaccel-settings.h"
#include "rawaccel-version.h"
#include "rawaccel-error.hpp"
+#include "rawaccel.hpp"
#pragma warning(push)
#pragma warning(disable:4245) // int -> DWORD conversion while passing CTL_CODE
namespace rawaccel {
- void io_control(DWORD code, void* in, DWORD in_size, void* out, DWORD out_size) {
- HANDLE ra_handle = INVALID_HANDLE_VALUE;
-
- ra_handle = CreateFileW(L"\\\\.\\rawaccel", 0, 0, 0, OPEN_EXISTING, 0, 0);
-
- if (ra_handle == INVALID_HANDLE_VALUE) {
- throw install_error();
- }
-
- DWORD dummy;
-
- BOOL success = DeviceIoControl(
- ra_handle,
- code,
- in,
- in_size,
- out,
- out_size,
- &dummy, // bytes returned
- NULL // overlapped structure
- );
-
- CloseHandle(ra_handle);
-
- if (!success) {
- throw std::system_error(GetLastError(), std::system_category(), "DeviceIoControl failed");
- }
- }
-
- settings read() {
- settings args;
- io_control(RA_READ, NULL, 0, &args, sizeof(settings));
- return args;
- }
-
-
- void write(const settings& args) {
- auto in_ptr = const_cast<settings*>(&args);
- io_control(RA_WRITE, in_ptr, sizeof(settings), NULL, 0);
- }
-
- version_t get_version() {
- version_t ver;
- io_control(RA_GET_VERSION, NULL, 0, &ver, sizeof(version_t));
- return ver;
- }
+ inline void io_control(DWORD code, void* in, DWORD in_size, void* out, DWORD out_size)
+ {
+ HANDLE ra_handle = INVALID_HANDLE_VALUE;
+
+ ra_handle = CreateFileW(L"\\\\.\\rawaccel", 0, 0, 0, OPEN_EXISTING, 0, 0);
+
+ if (ra_handle == INVALID_HANDLE_VALUE) {
+ throw install_error();
+ }
+
+ DWORD dummy;
+
+ BOOL success = DeviceIoControl(
+ ra_handle,
+ code,
+ in,
+ in_size,
+ out,
+ out_size,
+ &dummy, // bytes returned
+ NULL // overlapped structure
+ );
+
+ CloseHandle(ra_handle);
+
+ if (!success) {
+ throw sys_error("DeviceIoControl failed");
+ }
+ }
+
+ inline void read(io_t& args)
+ {
+ io_control(RA_READ, NULL, 0, &args, sizeof(io_t));
+ }
+
+ inline void write(const io_t& args)
+ {
+ io_control(RA_WRITE, const_cast<io_t*>(&args), sizeof(io_t), NULL, 0);
+ }
+
+ inline version_t get_version()
+ {
+ version_t v;
+
+ try {
+ io_control(RA_GET_VERSION, NULL, 0, &v, sizeof(version_t));
+ }
+ catch (const sys_error&) {
+ // assume request is not implemented (< 1.3)
+ v = { 0 };
+ }
+
+ return v;
+ }
+
+ inline version_t valid_version_or_throw()
+ {
+ auto v = get_version();
+
+ if (v < min_driver_version) {
+ throw error("reinstallation required");
+ }
+
+ if (version < v) {
+ throw error("newer driver is installed");
+ }
+
+ return v;
+ }
}
diff --git a/common/rawaccel-settings.h b/common/rawaccel-settings.h
deleted file mode 100644
index dedef6d..0000000
--- a/common/rawaccel-settings.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma once
-
-#include "vec2.h"
-#include "accel-base.hpp"
-
-#define MAX_DEV_ID_LEN 200
-
-namespace rawaccel {
-
- using milliseconds = double;
-
- inline constexpr int MAX_POLL_RATE_KHZ = 8;
- inline constexpr milliseconds DEFAULT_TIME_MIN = 1.0 / MAX_POLL_RATE_KHZ * 0.8;
- inline constexpr milliseconds WRITE_DELAY = 1000;
-
- enum class accel_mode {
- linear, classic, natural, naturalgain, power, motivity, noaccel
- };
-
- struct settings {
- double degrees_rotation = 0;
- double degrees_snap = 0;
- bool combine_mags = true;
- vec2<accel_mode> modes = { accel_mode::noaccel, accel_mode::noaccel };
- vec2<accel_args> argsv;
- vec2d sens = { 1, 1 };
- vec2d dir_multipliers = {};
- domain_args domain_args = {};
- vec2d range_weights = { 1, 1 };
- milliseconds time_min = DEFAULT_TIME_MIN;
- wchar_t device_id[MAX_DEV_ID_LEN] = {0};
- };
-
-}
diff --git a/common/rawaccel-validate.hpp b/common/rawaccel-validate.hpp
new file mode 100644
index 0000000..e9d1120
--- /dev/null
+++ b/common/rawaccel-validate.hpp
@@ -0,0 +1,185 @@
+#pragma once
+
+#include "rawaccel-base.hpp"
+#include "utility.hpp"
+
+namespace rawaccel {
+
+ struct valid_ret_t {
+ int count_x = 0;
+ int count_y = 0;
+ int count = 0;
+
+ explicit operator bool() const
+ {
+ return count == 0;
+ }
+ };
+
+ template <typename MsgHandler = noop>
+ valid_ret_t valid(const settings& args, MsgHandler fn = {})
+ {
+ valid_ret_t ret;
+
+ auto error = [&](auto msg) {
+ ++ret.count;
+ fn(msg);
+ };
+
+ auto check_accel = [&error](const accel_args& args) {
+ static_assert(SPACED_LUT_CAPACITY == 1025, "update error msg");
+
+ const auto& lut_args = args.spaced_args;
+
+ if (lut_args.partitions <= 0) {
+ error("lut partitions"" must be positive");
+ }
+
+ if (lut_args.mode == spaced_lut_mode::linear) {
+ if (lut_args.start <= 0) {
+ error("start"" must be positive");
+ }
+
+ if (lut_args.stop <= lut_args.start) {
+ error("stop must be greater than start");
+ }
+
+ if (lut_args.num_elements < 2 ||
+ lut_args.num_elements > 1025) {
+ error("num must be between 2 and 1025");
+ }
+ }
+ else if (lut_args.mode == spaced_lut_mode::binlog) {
+ int istart = static_cast<int>(lut_args.start);
+ int istop = static_cast<int>(lut_args.stop);
+
+ if (lut_args.start < -99) {
+ error("start is too small");
+ }
+ else if (lut_args.stop > 99) {
+ error("stop is too large");
+ }
+ else if (istart != lut_args.start || istop != lut_args.stop) {
+ error("start and stop must be integers");
+ }
+ else if (istop <= istart) {
+ error("stop must be greater than start");
+ }
+ else if (lut_args.num_elements <= 0) {
+ error("num"" must be positive");
+ }
+ else if (((lut_args.stop - lut_args.start) * lut_args.num_elements) >= 1025) {
+ error("binlog mode requires (num * (stop - start)) < 1025");
+ }
+ }
+
+ if (args.mode == accel_mode::arb_lookup) {
+ if (args.arb_args.length < 2) {
+ error("lookup mode requires at least 2 points");
+ }
+ }
+
+ if (args.offset < 0) {
+ error("offset can not be negative");
+ }
+
+ if (args.cap < 0) {
+ error("cap"" must not be negative");
+ }
+
+ if (args.growth_rate <= 0 ||
+ args.decay_rate <= 0 ||
+ args.accel_classic <= 0) {
+ error("acceleration"" must be positive");
+ }
+
+ if (args.motivity <= 1) {
+ error("motivity must be greater than 1");
+ }
+
+ if (args.power <= 1) {
+ error("power must be greater than 1");
+ }
+
+ if (args.scale <= 0) {
+ error("scale"" must be positive");
+ }
+
+ if (args.weight <= 0) {
+ error("weight"" must be positive");
+ }
+
+ if (args.exponent <= 0) {
+ error("exponent"" must be positive");
+ }
+
+ if (args.limit <= 0) {
+ error("limit"" must be positive");
+ }
+
+ if (args.midpoint <= 0) {
+ error("midpoint"" must be positive");
+ }
+
+ if (args.smooth < 0 || args.smooth > 1) {
+ error("smooth must be between 0 and 1");
+ }
+
+ };
+
+ check_accel(args.argsv.x);
+
+ if (!args.combine_mags) {
+ ret.count_x = ret.count;
+ check_accel(args.argsv.y);
+ ret.count_y = ret.count;
+ }
+
+ if (args.dpi <= 0) {
+ error("dpi"" must be positive");
+ }
+
+ if (args.speed_max < 0) {
+ error("speed cap is negative");
+ }
+ else if (args.speed_max < args.speed_min) {
+ error("max speed is less than min speed");
+ }
+
+ if (args.degrees_snap < 0 || args.degrees_snap > 45) {
+ error("snap angle must be between 0 and 45 degrees");
+ }
+
+ if (args.sens.x == 0 || args.sens.y == 0) {
+ error("sens multiplier is 0");
+ }
+
+ if (args.dom_args.domain_weights.x <= 0 ||
+ args.dom_args.domain_weights.y <= 0) {
+ error("domain weights"" must be positive");
+ }
+
+ if (args.dir_multipliers.x <= 0 || args.dir_multipliers.y <= 0) {
+ error("directional multipliers must be positive");
+ }
+
+ if (args.dom_args.lp_norm < 2) {
+ error("Lp norm is less than 2 (default=2)");
+ }
+
+ if (args.range_weights.x <= 0 || args.range_weights.y <= 0) {
+ error("range weights"" must be positive");
+ }
+
+ if (args.time_min <= 0) {
+ error("minimum time"" must be positive");
+ }
+
+ if (args.time_max < args.time_min) {
+ error("max time is less than min time");
+ }
+
+ return ret;
+ }
+
+}
diff --git a/common/rawaccel-version.h b/common/rawaccel-version.h
index ce53b30..384ba6f 100644
--- a/common/rawaccel-version.h
+++ b/common/rawaccel-version.h
@@ -1,26 +1,36 @@
#pragma once
#define RA_VER_MAJOR 1
-#define RA_VER_MINOR 4
-#define RA_VER_PATCH 4
+#define RA_VER_MINOR 5
+#define RA_VER_PATCH 0
#define RA_OS "Win7+"
-#define M_STR_HELPER(x) #x
-#define M_STR(x) M_STR_HELPER(x)
+#define RA_M_STR_HELPER(x) #x
+#define RA_M_STR(x) RA_M_STR_HELPER(x)
-#define RA_VER_STRING M_STR(RA_VER_MAJOR) "." M_STR(RA_VER_MINOR) "." M_STR(RA_VER_PATCH)
+#define RA_VER_STRING RA_M_STR(RA_VER_MAJOR) "." RA_M_STR(RA_VER_MINOR) "." RA_M_STR(RA_VER_PATCH)
namespace rawaccel {
- struct version_t {
- int major;
- int minor;
- int patch;
- };
-
+ struct version_t {
+ int major;
+ int minor;
+ int patch;
+ };
+
+ constexpr bool operator<(const version_t& lhs, const version_t& rhs)
+ {
+ return (lhs.major != rhs.major) ?
+ (lhs.major < rhs.major) :
+ (lhs.minor != rhs.minor) ?
+ (lhs.minor < rhs.minor) :
+ (lhs.patch < rhs.patch) ;
+ }
+
+ inline constexpr version_t version = { RA_VER_MAJOR, RA_VER_MINOR, RA_VER_PATCH };
#ifndef _KERNEL_MODE
- inline constexpr version_t min_driver_version = { 1, 4, 0 };
+ inline constexpr version_t min_driver_version = { 1, 5, 0 };
#endif
}
diff --git a/common/rawaccel.hpp b/common/rawaccel.hpp
index e28cd92..fb2d81a 100644
--- a/common/rawaccel.hpp
+++ b/common/rawaccel.hpp
@@ -1,419 +1,179 @@
#pragma once
-#define _USE_MATH_DEFINES
-#include <math.h>
-
-#include "rawaccel-settings.h"
-#include "x64-util.hpp"
-
-#include "accel-linear.hpp"
-#include "accel-classic.hpp"
-#include "accel-natural.hpp"
-#include "accel-naturalgain.hpp"
-#include "accel-power.hpp"
-#include "accel-motivity.hpp"
-#include "accel-noaccel.hpp"
+#include "accel-invoke.hpp"
namespace rawaccel {
- /// <summary> Struct to hold vector rotation details. </summary>
- struct rotator {
-
- /// <summary> Rotational vector, which points in the direction of the post-rotation positive x axis. </summary>
- vec2d rot_vec = { 1, 0 };
-
- /// <summary>
- /// Rotates given input vector according to struct's rotational vector.
- /// </summary>
- /// <param name="input">Input vector to be rotated</param>
- /// <returns>2d vector of rotated input.</returns>
- inline vec2d apply(const vec2d& input) const {
- return {
- input.x * rot_vec.x - input.y * rot_vec.y,
- input.x * rot_vec.y + input.y * rot_vec.x
- };
- }
-
- rotator(double degrees) {
- double rads = degrees * M_PI / 180;
- rot_vec = { cos(rads), sin(rads) };
- }
-
- rotator() = default;
- };
-
- struct snapper {
- double threshold = 0;
-
- inline vec2d apply(const vec2d& input) const {
- if (input.x != 0 && input.y != 0) {
- double angle = fabs(atan(input.y / input.x));
- auto mag = [&] { return sqrtsd(input.x * input.x + input.y * input.y); };
-
- if (angle > M_PI_2 - threshold) return { 0, _copysign(mag(), input.y) };
- if (angle < threshold) return { _copysign(mag(), input.x), 0 };
- }
-
- return input;
- }
-
- snapper(double degrees) : threshold(minsd(fabs(degrees), 45) * M_PI / 180) {}
-
- snapper() = default;
- };
-
- /// <summary> Struct to hold clamp (min and max) details for acceleration application </summary>
- struct accel_scale_clamp {
- double lo = 0;
- double hi = 1e9;
-
- /// <summary>
- /// Clamps given input to min at lo, max at hi.
- /// </summary>
- /// <param name="scale">Double to be clamped</param>
- /// <returns>Clamped input as double</returns>
- inline double operator()(double scale) const {
- return clampsd(scale, lo, hi);
- }
+ inline vec2d direction(double degrees)
+ {
+ double radians = degrees * PI / 180;
+ return { cos(radians), sin(radians) };
+ }
- accel_scale_clamp(double cap) {
- if (cap <= 0) {
- // use default, effectively uncapped accel
- return;
- }
+ constexpr vec2d rotate(const vec2d& v, const vec2d& direction)
+ {
+ return {
+ v.x * direction.x - v.y * direction.y,
+ v.x * direction.y + v.y * direction.x
+ };
+ }
- if (cap < 1) {
- // assume negative accel
- lo = cap;
- hi = 1;
- }
- else hi = cap;
- }
+ inline double magnitude(const vec2d& v)
+ {
+ return sqrt(v.x * v.x + v.y * v.y);
+ }
- accel_scale_clamp() = default;
- };
-
- template <typename Visitor, typename Variant>
- inline auto visit_accel(Visitor vis, Variant&& var) {
- switch (var.tag) {
- case accel_mode::linear: return vis(var.u.linear);
- case accel_mode::classic: return vis(var.u.classic);
- case accel_mode::natural: return vis(var.u.natural);
- case accel_mode::naturalgain: return vis(var.u.naturalgain);
- case accel_mode::power: return vis(var.u.power);
- case accel_mode::motivity: return vis(var.u.motivity);
- default: return vis(var.u.noaccel);
- }
+ inline double lp_distance(const vec2d& v, double p)
+ {
+ return pow(pow(v.x, p) + pow(v.y, p), 1 / p);
}
- struct accel_variant {
- si_pair* lookup;
+ class mouse_modifier {
+ public:
+ enum accel_distance_mode : unsigned char {
+ separate,
+ max,
+ Lp,
+ euclidean,
+ };
- accel_mode tag = accel_mode::noaccel;
+ bool apply_rotate = false;
+ bool compute_ref_angle = false;
+ bool apply_snap = false;
+ bool cap_speed = false;
+ accel_distance_mode dist_mode = euclidean;
+ bool apply_directional_weight = false;
+ bool apply_dir_mul_x = false;
+ bool apply_dir_mul_y = false;
- union union_t {
- accel_linear linear;
- accel_classic classic;
- accel_natural natural;
- accel_naturalgain naturalgain;
- accel_power power;
- accel_motivity motivity;
- accel_noaccel noaccel = {};
- } u = {};
+ vec2d rot_vec = { 1, 0 };
+ double snap = 0;
+ double dpi_norm_factor = 1;
+ double speed_min = 0;
+ double speed_max = 0;
+ vec2d domain_weights = { 1, 1 };
+ double p = 2;
+ vec2d range_weights = { 1, 1 };
+ vec2d directional_multipliers = { 1, 1 };
+ vec2d sensitivity = { 1, 1 };
+ vec2<accel_union> accel;
- accel_variant(const accel_args& args, accel_mode mode, si_pair* lut = nullptr) :
- tag(mode), lookup(lut)
+#ifdef _KERNEL_MODE
+ __forceinline
+#endif
+ void modify(vec2d& in, const vec2<accel_invoker>& inv, milliseconds time = 1) const
{
- visit_accel([&](auto& impl) {
- impl = { args };
- }, *this);
+ double ips_factor = dpi_norm_factor / time;
+ double reference_angle = 0;
- if (lookup && tag == accel_mode::motivity) {
- u.motivity.fn.fill(lookup);
- }
+ if (apply_rotate) in = rotate(in, rot_vec);
- }
-
- inline double apply(double speed) const {
- if (lookup && tag == accel_mode::motivity) {
- return u.motivity.fn.apply(lookup, speed);
+ if (compute_ref_angle && in.y != 0) {
+ if (in.x == 0) {
+ reference_angle = PI / 2;
+ }
+ else {
+ reference_angle = atan(fabs(in.y / in.x));
+
+ if (apply_snap) {
+ if (reference_angle > PI / 2 - snap) {
+ reference_angle = PI / 2;
+ in = { 0, _copysign(magnitude(in), in.y) };
+ }
+ else if (reference_angle < snap) {
+ reference_angle = 0;
+ in = { _copysign(magnitude(in), in.x), 0 };
+ }
+ }
+ }
}
- return visit_accel([=](auto&& impl) {
- return impl(speed);
- }, *this);
- }
-
- accel_variant() = default;
- };
-
- /// <summary> Struct to hold information about applying a gain cap. </summary>
- struct velocity_gain_cap {
-
- // <summary> The minimum speed past which gain cap is applied. </summary>
- double threshold = 0;
-
- // <summary> The gain at the point of cap </summary>
- double slope = 0;
-
- // <summary> The intercept for the line with above slope to give continuous velocity function </summary>
- double intercept = 0;
-
- /// <summary>
- /// Initializes a velocity gain cap for a certain speed threshold
- /// by estimating the slope at the threshold and creating a line
- /// with that slope for output velocity calculations.
- /// </summary>
- /// <param name="speed"> The speed at which velocity gain cap will kick in </param>
- /// <param name="accel"> The accel implementation used in the containing accel_variant </param>
- velocity_gain_cap(double speed, const accel_variant& accel)
- {
- if (speed <= 0) return;
-
- // Estimate gain at cap point by taking line between two input vs output velocity points.
- // First input velocity point is at cap; for second pick a velocity a tiny bit larger.
- double speed_second = 1.001 * speed;
- double speed_diff = speed_second - speed;
-
- // Return if by glitch or strange values the difference in points is 0.
- if (speed_diff == 0) return;
-
- // Find the corresponding output velocities for the two points.
- double out_first = accel.apply(speed) * speed;
- double out_second = accel.apply(speed_second) * speed_second;
-
- // Calculate slope and intercept from two points.
- slope = (out_second - out_first) / speed_diff;
- intercept = out_first - slope * speed;
-
- threshold = speed;
- }
-
- /// <summary>
- /// Applies velocity gain cap to speed.
- /// Returns scale value by which to multiply input to place on gain cap line.
- /// </summary>
- /// <param name="speed"> Speed to be capped </param>
- /// <returns> Scale multiplier for input </returns>
- inline double apply(double speed) const {
- return slope + intercept / speed;
- }
-
- /// <summary>
- /// Whether gain cap should be applied to given speed.
- /// </summary>
- /// <param name="speed"> Speed to check against threshold. </param>
- /// <returns> Whether gain cap should be applied. </returns>
- inline bool should_apply(double speed) const {
- return threshold > 0 && speed > threshold;
- }
-
- velocity_gain_cap() = default;
- };
-
- struct accelerator {
- accel_variant accel;
- velocity_gain_cap gain_cap;
- accel_scale_clamp clamp;
- double output_speed_cap = 0;
-
- accelerator(const accel_args& args, accel_mode mode, si_pair* lut = nullptr) :
- accel(args, mode, lut), gain_cap(args.gain_cap, accel), clamp(args.scale_cap)
- {
- output_speed_cap = maxsd(args.speed_cap, 0);
- }
-
- inline double apply(double speed) const {
- double scale;
-
- if (gain_cap.should_apply(speed)) {
- scale = gain_cap.apply(speed);
- }
- else {
- scale = accel.apply(speed);
+ if (cap_speed) {
+ double speed = magnitude(in) * ips_factor;
+ double ratio = clampsd(speed, speed_min, speed_max) / speed;
+ in.x *= ratio;
+ in.y *= ratio;
}
- scale = clamp(scale);
+ vec2d abs_weighted_vel = {
+ fabs(in.x * ips_factor * domain_weights.x),
+ fabs(in.y * ips_factor * domain_weights.y)
+ };
- if (output_speed_cap > 0 && (scale * speed) > output_speed_cap) {
- scale = output_speed_cap / speed;
+ if (dist_mode == separate) {
+ in.x *= inv.x.invoke(accel.x, abs_weighted_vel.x, range_weights.x);
+ in.y *= inv.y.invoke(accel.y, abs_weighted_vel.y, range_weights.y);
}
+ else {
+ double speed;
- return scale;
- }
+ if (dist_mode == max) {
+ speed = maxsd(abs_weighted_vel.x, abs_weighted_vel.y);
+ }
+ else if (dist_mode == Lp) {
+ speed = lp_distance(abs_weighted_vel, p);
+ }
+ else {
+ speed = magnitude(abs_weighted_vel);
+ }
- accelerator() = default;
- };
+ double weight = range_weights.x;
- struct weighted_distance {
- double p = 2.0;
- double p_inverse = 0.5;
- bool lp_norm_infinity = false;
- double sigma_x = 1.0;
- double sigma_y = 1.0;
+ if (apply_directional_weight) {
+ double diff = range_weights.y - range_weights.x;
+ weight += 2 / PI * reference_angle * diff;
+ }
- weighted_distance(const domain_args& args)
- {
- sigma_x = args.domain_weights.x;
- sigma_y = args.domain_weights.y;
- if (args.lp_norm <= 0)
- {
- lp_norm_infinity = true;
- p = 0.0;
- p_inverse = 0.0;
- }
- else
- {
- lp_norm_infinity = false;
- p = args.lp_norm;
- p_inverse = 1 / args.lp_norm;
+ double scale = inv.x.invoke(accel.x, speed, weight);
+ in.x *= scale;
+ in.y *= scale;
}
- }
-
- inline double calculate(double x, double y)
- {
- double abs_x = fabs(x);
- double abs_y = fabs(y);
-
- if (lp_norm_infinity) return maxsd(abs_x, abs_y);
-
- double x_scaled = abs_x * sigma_x;
- double y_scaled = abs_y * sigma_y;
-
- if (p == 2) return sqrtsd(x_scaled * x_scaled + y_scaled * y_scaled);
- else return pow(pow(x_scaled, p) + pow(y_scaled, p), p_inverse);
- }
-
- weighted_distance() = default;
- };
-
- struct direction_weight {
- double diff = 0.0;
- double start = 1.0;
- bool should_apply = false;
-
- direction_weight(const vec2d& thetas)
- {
- diff = thetas.y - thetas.x;
- start = thetas.x;
-
- should_apply = diff != 0;
- }
-
- inline double atan_scale(double x, double y)
- {
- return M_2_PI * atan2(fabs(y), fabs(x));
- }
-
- inline double apply(double x, double y)
- {
- return atan_scale(x, y) * diff + start;
- }
- direction_weight() = default;
- };
-
- /// <summary> Struct to hold variables and methods for modifying mouse input </summary>
- struct mouse_modifier {
- bool apply_rotate = false;
- bool apply_snap = false;
- bool apply_accel = false;
- bool combine_magnitudes = true;
- rotator rotate;
- snapper snap;
- weighted_distance distance;
- direction_weight directional;
- vec2<accelerator> accels;
- vec2d sensitivity = { 1, 1 };
- vec2d directional_multipliers = {};
-
- mouse_modifier(const settings& args, vec2<si_pair*> luts = {}) :
- combine_magnitudes(args.combine_mags)
- {
- if (args.degrees_rotation != 0) {
- rotate = rotator(args.degrees_rotation);
- apply_rotate = true;
+ if (apply_dir_mul_x && in.x < 0) {
+ in.x *= directional_multipliers.x;
}
-
- if (args.degrees_snap != 0) {
- snap = snapper(args.degrees_snap);
- apply_snap = true;
- }
-
- if (args.sens.x != 0) sensitivity.x = args.sens.x;
- if (args.sens.y != 0) sensitivity.y = args.sens.y;
- directional_multipliers.x = fabs(args.dir_multipliers.x);
- directional_multipliers.y = fabs(args.dir_multipliers.y);
-
- if ((combine_magnitudes && args.modes.x == accel_mode::noaccel) ||
- (args.modes.x == accel_mode::noaccel &&
- args.modes.y == accel_mode::noaccel)) {
- return;
+ if (apply_dir_mul_y && in.y < 0) {
+ in.y *= directional_multipliers.y;
}
- accels.x = accelerator(args.argsv.x, args.modes.x, luts.x);
- accels.y = accelerator(args.argsv.y, args.modes.y, luts.y);
-
- distance = weighted_distance(args.domain_args);
- directional = direction_weight(args.range_weights);
-
- apply_accel = true;
- }
-
- void modify(vec2d& movement, milliseconds time) {
- apply_rotation(movement);
- apply_angle_snap(movement);
- apply_acceleration(movement, [=] { return time; });
- apply_sensitivity(movement);
- }
-
- inline void apply_rotation(vec2d& movement) {
- if (apply_rotate) movement = rotate.apply(movement);
+ in.x *= sensitivity.x;
+ in.y *= sensitivity.y;
}
- inline void apply_angle_snap(vec2d& movement) {
- if (apply_snap) movement = snap.apply(movement);
- }
-
- template <typename TimeSupplier>
- inline void apply_acceleration(vec2d& movement, TimeSupplier time_supp) {
- if (apply_accel) {
- milliseconds time = time_supp();
-
- if (combine_magnitudes) {
- double mag = distance.calculate(movement.x, movement.y);
- double speed = mag / time;
- double scale = accels.x.apply(speed);
-
- if (directional.should_apply)
- {
- scale = (scale - 1)*directional.apply(movement.x, movement.y) + 1;
- }
-
- movement.x *= scale;
- movement.y *= scale;
- }
- else {
- movement.x *= accels.x.apply(fabs(movement.x) / time);
- movement.y *= accels.y.apply(fabs(movement.y) / time);
- }
- }
- }
-
- inline void apply_sensitivity(vec2d& movement) {
- movement.x *= sensitivity.x;
- movement.y *= sensitivity.y;
+ mouse_modifier(const settings& args) :
+ rot_vec(direction(args.degrees_rotation)),
+ snap(args.degrees_snap * PI / 180),
+ dpi_norm_factor(1000 / args.dpi),
+ speed_min(args.speed_min),
+ speed_max(args.speed_max),
+ p(args.dom_args.lp_norm),
+ domain_weights(args.dom_args.domain_weights),
+ range_weights(args.range_weights),
+ directional_multipliers(args.dir_multipliers),
+ sensitivity(args.sens),
+ accel({ { args.argsv.x }, { args.argsv.y } })
+ {
+ cap_speed = speed_max > 0 && speed_min <= speed_max;
+ apply_rotate = rot_vec.x != 1;
+ apply_snap = snap != 0;
+ apply_directional_weight = range_weights.x != range_weights.y;
+ compute_ref_angle = apply_snap || apply_directional_weight;
+ apply_dir_mul_x = directional_multipliers.x != 1;
+ apply_dir_mul_y = directional_multipliers.y != 1;
- if (directional_multipliers.x > 0 && movement.x < 0) {
- movement.x *= directional_multipliers.x;
- }
- if (directional_multipliers.y > 0 && movement.y < 0) {
- movement.y *= directional_multipliers.y;
- }
+ if (!args.combine_mags) dist_mode = separate;
+ else if (p >= MAX_NORM) dist_mode = max;
+ else if (p > 2) dist_mode = Lp;
+ else dist_mode = euclidean;
}
mouse_modifier() = default;
};
+ struct io_t {
+ settings args;
+ mouse_modifier mod;
+ };
+
} // rawaccel
diff --git a/common/utility-rawinput.hpp b/common/utility-rawinput.hpp
index c3a4576..f04b2c1 100644
--- a/common/utility-rawinput.hpp
+++ b/common/utility-rawinput.hpp
@@ -66,7 +66,7 @@ void rawinput_foreach_dev_with_id(Func fn, bool with_instance_id = false,
if (!with_instance_id) {
auto instance_delim_pos = id.find_last_of('\\');
- if(instance_delim_pos != std::string::npos) id.resize(instance_delim_pos);
+ if (instance_delim_pos != std::string::npos) id.resize(instance_delim_pos);
}
fn(dev, id);
diff --git a/common/utility.hpp b/common/utility.hpp
new file mode 100644
index 0000000..cbd19e3
--- /dev/null
+++ b/common/utility.hpp
@@ -0,0 +1,88 @@
+#pragma once
+
+namespace rawaccel {
+
+ constexpr double minsd(double a, double b)
+ {
+ return (a < b) ? a : b;
+ }
+
+ constexpr double maxsd(double a, double b)
+ {
+ return (b < a) ? a : b;
+ }
+
+ constexpr double clampsd(double v, double lo, double hi)
+ {
+ return minsd(maxsd(v, lo), hi);
+ }
+
+ template <typename T>
+ constexpr const T& min(const T& a, const T& b)
+ {
+ return (b < a) ? b : a;
+ }
+
+ template <typename T>
+ constexpr const T& max(const T& a, const T& b)
+ {
+ return (b < a) ? a : b;
+ }
+
+ template <typename T>
+ constexpr const T& clamp(const T& v, const T& lo, const T& hi)
+ {
+ return (v < lo) ? lo : (hi < v) ? hi : v;
+ }
+
+ constexpr double lerp(double a, double b, double t)
+ {
+ double x = a + t * (b - a);
+ if ((t > 1) == (a < b)) {
+ return maxsd(x, b);
+ }
+ return minsd(x, b);
+ }
+
+ // returns the unbiased exponent of x if x is normal
+ inline int ilogb(double x)
+ {
+ union { double f; unsigned long long i; } u = { x };
+ return static_cast<int>((u.i >> 52) & 0x7ff) - 0x3ff;
+ }
+
+ // returns x * 2^n if n is in [-1022, 1023]
+ inline double scalbn(double x, int n)
+ {
+ union { double f; unsigned long long i; } u;
+ u.i = static_cast<unsigned long long>(0x3ff + n) << 52;
+ return x * u.f;
+ }
+
+ inline bool infnan(double x)
+ {
+ return ilogb(x) == 0x400;
+ }
+
+ struct noop {
+ template <typename... Ts>
+ constexpr void operator()(Ts&&...) const noexcept {}
+ };
+
+ template <typename T> struct remove_ref { using type = T; };
+ template <typename T> struct remove_ref<T&> { using type = T; };
+ template <typename T> struct remove_ref<T&&> { using type = T; };
+
+ template <typename T>
+ using remove_ref_t = typename remove_ref<T>::type;
+
+ template <typename T, typename U>
+ struct is_same { static constexpr bool value = false; };
+
+ template <typename T>
+ struct is_same<T, T> { static constexpr bool value = true; };
+
+ template <typename T, typename U>
+ inline constexpr bool is_same_v = is_same<T, U>::value;
+
+}
diff --git a/common/x64-util.hpp b/common/x64-util.hpp
deleted file mode 100644
index 40bc7c4..0000000
--- a/common/x64-util.hpp
+++ /dev/null
@@ -1,30 +0,0 @@
-#pragma once
-
-#ifdef _MANAGED
-
-#include <math.h>
-inline double sqrtsd(double val) { return sqrt(val); }
-
-#else
-
-#include <emmintrin.h>
-inline double sqrtsd(double val) {
- __m128d src = _mm_load_sd(&val);
- __m128d dst = _mm_sqrt_sd(src, src);
- _mm_store_sd(&val, dst);
- return val;
-}
-
-#endif
-
-inline constexpr double minsd(double a, double b) {
- return (a < b) ? a : b;
-}
-
-inline constexpr double maxsd(double a, double b) {
- return (b < a) ? a : b;
-}
-
-inline constexpr double clampsd(double v, double lo, double hi) {
- return minsd(maxsd(v, lo), hi);
-}
diff --git a/converter/converter.cpp b/converter/converter.cpp
index 230e1be..ad92f8c 100644
--- a/converter/converter.cpp
+++ b/converter/converter.cpp
@@ -1,3 +1,5 @@
+#include <rawaccel-base.hpp>
+
#include <array>
#include <charconv>
#include <filesystem>
@@ -7,9 +9,8 @@
#include <sstream>
#include <string>
-#include <rawaccel-settings.h>
-
using namespace System;
+using namespace System::IO;
using namespace System::Runtime::InteropServices;
using namespace Newtonsoft::Json;
@@ -108,7 +109,7 @@ auto make_extractor(const ia_settings_t& ia_settings) {
};
}
-ra::accel_args convert_natural(const ia_settings_t& ia_settings) {
+ra::accel_args convert_natural(const ia_settings_t& ia_settings, bool legacy) {
auto get = make_extractor(ia_settings);
double accel = get("Acceleration").value_or(0);
@@ -117,8 +118,13 @@ ra::accel_args convert_natural(const ia_settings_t& ia_settings) {
double prescale = get("Pre-ScaleX").value_or(1);
ra::accel_args args;
+
args.limit = 1 + std::abs(cap - sens) / sens;
- args.accel = accel * prescale / sens;
+ args.decay_rate = accel * prescale / sens;
+ args.offset = get("Offset").value_or(0);
+ args.mode = ra::accel_mode::natural;
+ args.legacy = legacy;
+
return args;
}
@@ -130,25 +136,16 @@ ra::accel_args convert_quake(const ia_settings_t& ia_settings, bool legacy) {
double cap = get("SensitivityCap").value_or(0);
double sens = get("Sensitivity").value_or(1);
double prescale = get("Pre-ScaleX").value_or(1);
- double offset = get("Offset").value_or(0);
-
- ra::accel_args args;
- double powm1 = power - 1;
- double rpowm1 = 1 / powm1;
- double accel_b = std::pow(accel * prescale, powm1) / sens;
- args.accel = std::pow(accel_b, rpowm1);
- args.exponent = power;
- args.legacy_offset = legacy;
- args.offset = offset;
- double cap_converted = cap / sens;
+ ra::accel_args args;
- if (legacy || cap_converted <= 1) {
- args.scale_cap = cap_converted;
- }
- else {
- args.gain_cap = offset + std::pow(cap_converted - 1, rpowm1) / args.accel;
- }
+ double accel_b = std::pow(accel * prescale, power - 1) / sens;
+ args.accel_classic = std::pow(accel_b, 1 / (power - 1));
+ args.cap = cap / sens;
+ args.power = power;
+ args.offset = get("Offset").value_or(0);
+ args.mode = ra::accel_mode::classic;
+ args.legacy = legacy;
return args;
}
@@ -156,7 +153,7 @@ ra::accel_args convert_quake(const ia_settings_t& ia_settings, bool legacy) {
bool try_convert(const ia_settings_t& ia_settings) {
auto get = make_extractor(ia_settings);
- ra::settings ra_settings;
+ ra::settings& ra_settings = *(new ra::settings());
ra_settings.degrees_rotation = get("Angle", "AngleAdjustment").value_or(0);
ra_settings.sens = {
@@ -168,17 +165,15 @@ bool try_convert(const ia_settings_t& ia_settings) {
switch (static_cast<IA_MODES_ENUM>(mode)) {
case IA_QL: {
- bool legacy = !ask("We recommend trying out our new cap and offset styles.\n"
- "Use new cap and offset?");
- ra_settings.modes.x = ra::accel_mode::classic;
+ bool legacy = !ask("We recommend trying out a new smooth cap style.\n"
+ "Use new cap style?");
ra_settings.argsv.x = convert_quake(ia_settings, legacy);
break;
}
case IA_NAT: {
- bool nat_gain = ask("Raw Accel offers a new mode that you might like more than Natural.\n"
+ bool legacy = !ask("Raw Accel offers a new mode that you might like more than Natural.\n"
"Wanna try it out now?");
- ra_settings.modes.x = nat_gain ? ra::accel_mode::naturalgain : ra::accel_mode::natural;
- ra_settings.argsv.x = convert_natural(ia_settings);
+ ra_settings.argsv.x = convert_natural(ia_settings, legacy);
break;
}
case IA_LOG: {
@@ -187,9 +182,9 @@ bool try_convert(const ia_settings_t& ia_settings) {
}
default: return false;
}
-
+
DriverSettings^ new_settings = Marshal::PtrToStructure<DriverSettings^>((IntPtr)&ra_settings);
- auto errors = DriverInterop::GetSettingsErrors(new_settings);
+ SettingsErrors^ errors = gcnew SettingsErrors(new_settings);
if (!errors->Empty()) {
Console::WriteLine("Bad settings: {0}", errors);
@@ -197,21 +192,16 @@ bool try_convert(const ia_settings_t& ia_settings) {
}
Console::Write("Sending to driver... ");
- DriverInterop::Write(new_settings);
+ (gcnew ManagedAccel(gcnew ExtendedSettings(new_settings)))->Activate();
Console::WriteLine("done");
Console::Write("Generating settings.json... ");
- auto json = JsonConvert::SerializeObject(new_settings, Formatting::Indented);
- System::IO::File::WriteAllText("settings.json", json);
+ File::WriteAllText("settings.json", RaConvert::Settings(new_settings));
Console::WriteLine("done");
return true;
}
-public ref struct ASSEMBLY {
- static initonly Version^ VERSION = ASSEMBLY::typeid->Assembly->GetName()->Version;
-};
-
int main()
{
auto close_prompt = [] {
@@ -221,9 +211,9 @@ int main()
};
try {
- VersionHelper::ValidateAndGetDriverVersion(ASSEMBLY::VERSION);
+ VersionHelper::ValidOrThrow();
}
- catch (VersionException^ ex) {
+ catch (InteropException^ ex) {
Console::WriteLine(ex->Message);
close_prompt();
}
@@ -239,7 +229,7 @@ int main()
entry.path().extension() == IA_PROFILE_EXT) {
opt_path = entry;
break;
- }
+ }
}
}
diff --git a/doc/Guide.md b/doc/Guide.md
index 23b4be1..d640451 100644
--- a/doc/Guide.md
+++ b/doc/Guide.md
@@ -82,6 +82,9 @@ The authors of this program feel that Whole is the best style for most users, bu
## Features
+### Gain Switch
+The acceleration curve styles below (see "Acceleration Styles") each describe a certain shape mathematically. The gain switch determines whether that shape is applied in the sensitivity graph or the gain graph. For styles Linear, Classic, and Power, this setting does not change the possible shapes of the velocity curve - that is, for any particular settings with the gain switch set to Sensitivity, there is a different set of settings that will replicate the exact same velocity curve (output for a given hand motion) with the switch set to Gain. For styles Natural, Jump, and Motivity, this is not true, and the gain switch allows new velocity curves for each style.
+
### Offsets
An offset, sometimes called a threshold, is a speed in counts before acceleration "kicks in". The legacy way of applying an offset is having a multiplier of 1 below and at the offset, and applying the sensitivity of (speed-offset) above. This legacy "sensitivity offset" is still available but causes a large discontuinity in gain at the point of offset, leading to non-smooth feeling at offset cross. The new default "gain offset" does a little extra math to simply shift the gain graph by the offset amount without any discontinuity. This feels smoother and has almost no effect on sensitivity. The theory behind "gain offsets" is developed in [this document](https://docs.google.com/document/d/1P6LygpeEazyHfjVmaEygCsyBjwwW2A-eMBl81ZfxXZk).
@@ -103,7 +106,7 @@ The Raw Accel GUI reads the output of the raw input stream, and thus the output
This option does not scale your acceleration curve in any way. Rather, DPI scales the set of points used to graph your curve, and shows you a window of input speed relevant for your chosen DPI. The poll rate is used as a safeguard for the Last Mouse Move points and therefore should be set for accuracy in that measurement.
## Acceleration Styles
-The examples of various types below show some typical settings, without a cap or offset, for a mouse at 1200 DPI and 1000 hz.
+The examples of various types below show some typical settings, without a cap or offset, for a mouse at 1600 DPI and 1000 hz.
### Linear
This is simplest style used by most; it is simply a line rising at a given rate. This is a good choice for new users.
@@ -118,13 +121,20 @@ This is the style found in CS:GO and Source Engine games (m_customaccel 3). The
![PowerExample](images/power_example.png)
### Natural & NaturalGain
-Natural is a style found in the game Diabotical. It features a concave curve which starts at 1 and approaches some maximum sensitivity. This style is unique and useful but causes an ugly dip in the gain graph. For this reason we have created the NaturalGain style, which recreates the Natural style shape in the gain graph without any dips. We recommend users use the NaturalGain style instead of the Natural style; on switch some small tweaks may be needed since for any particular settings the NaturalGain is slightly slower to ramp up than the Natural style. NaturalGain is another excellent choice for new users.
-![NaturalExample](images/natural_example.png)
+Natural features a concave curve which starts at 1 and approaches some maximum sensitivity. The sensitivity version of this curve can be found in the game Diabotical. This style is unique and useful but causes an ugly dip in the gain graph. The gain version of this curve recreates the Natural style shape in the gain graph without any dips and therefore we recommend this version. Natural is an excellent choice for new users due to only needing a two intuitive parameters which achieve what many users are looking for.
![NaturalGainExample](images/natural_gain_example.png)
+### Jump
+ This style applies one sensitivity below a certain threshold, and another above it. It can be useful for those who want one constant sensitivity and gain for slow hand motions and a different constant sensitivity or gain for fast hand motions. Users can set a "smooth" parameter which dictates whether the jump happens instaneously (at smooth 0) or with a slight tailing in and out (smooth 1) leading to a small sigmoid shape (s-shape). (Note that this "smooth" parameter has nothing to do with averaging over mouse counts like in sensor smoothing on mice or mouse smoothing in games.)
+![NaturalGainExample](images/jump_example.png)
+
### Motivity
-This curve looks like an "S" with the top half bigger than the bottom. Mathematically it's a "Sigmoid function on a log-log plot". A user can set the "midpoint" of the S, the "acceleration" (i.e. slantedness) of the S, and the "motivity". "Motivity" sets min and max sensitivity, where the maximum is just "motivity", and the minimum is "1/motivity." (Gain is 1 at the midpoint.) This curve is calculated and stored in a lookup table before applying acceleration, which makes the gain graph look a little funny. This is one author's favorite curve, and an excellent choice for power users and new users who don't mind playing with the settings a little.
+This curve looks like an "S" with the top half bigger than the bottom. Mathematically it's a "Sigmoid function on a log-log plot". A user can set the "midpoint" of the S, the "growth rate" (i.e. slantedness) of the S, and the "motivity". "Motivity" sets min and max sensitivity, where the maximum is just "motivity", and the minimum is "1/motivity." (Sensitivity or gain is 1 at the midpoint.) The gain version of this curve is calculated and stored in a lookup table before applying acceleration, which makes the gain graph look a little funny. This is an excellent choice for power users and new users who don't mind playing with the settings a little.
![MotivityExample](images/motivity_example.png)
+### Look Up Table
+This curve style is a blank canvas on which to create a curve. It allows the user to define the points which will make up the curve. For this reason, this mode is only for experts who know exactly what they want. Points can be supplied in the GUI according to format x1,y1;x2,y2;...xn.yn or in the settings.json in json format. The default Windows mouse acceleration settings (Enhanced Pointer Precision) can be very closely emulated with this style, using points: "1.505035,0.85549892;4.375,3.30972978;13.51,15.17478447;140,354.7026875;".
+![LUTExample](images/lut_example.png)
+
## Further Help
Further help and frequently asked questions can be found in the [FAQ](https://github.com/a1xd/rawaccel/blob/master/doc/FAQ.md).
diff --git a/doc/images/LUT_example.png b/doc/images/LUT_example.png
new file mode 100644
index 0000000..71f9ce3
--- /dev/null
+++ b/doc/images/LUT_example.png
Binary files differ
diff --git a/doc/images/classic_example.png b/doc/images/classic_example.png
index e626e86..58ca915 100644
--- a/doc/images/classic_example.png
+++ b/doc/images/classic_example.png
Binary files differ
diff --git a/doc/images/jump_example.png b/doc/images/jump_example.png
new file mode 100644
index 0000000..770bab2
--- /dev/null
+++ b/doc/images/jump_example.png
Binary files differ
diff --git a/doc/images/linear_example.png b/doc/images/linear_example.png
index 5530529..be1410e 100644
--- a/doc/images/linear_example.png
+++ b/doc/images/linear_example.png
Binary files differ
diff --git a/doc/images/motivity_example.png b/doc/images/motivity_example.png
index 521bf85..6041ec6 100644
--- a/doc/images/motivity_example.png
+++ b/doc/images/motivity_example.png
Binary files differ
diff --git a/doc/images/natural_gain_example.png b/doc/images/natural_gain_example.png
index 48a05e4..3b66dbe 100644
--- a/doc/images/natural_gain_example.png
+++ b/doc/images/natural_gain_example.png
Binary files differ
diff --git a/doc/images/power_example.png b/doc/images/power_example.png
index c8a58c9..414a252 100644
--- a/doc/images/power_example.png
+++ b/doc/images/power_example.png
Binary files differ
diff --git a/driver/driver.cpp b/driver/driver.cpp
index 0c21f34..feace77 100644
--- a/driver/driver.cpp
+++ b/driver/driver.cpp
@@ -1,8 +1,9 @@
+#include "driver.h"
+
#include <rawaccel.hpp>
#include <rawaccel-io-def.h>
#include <rawaccel-version.h>
-#include "driver.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, DriverEntry)
@@ -11,18 +12,18 @@
#pragma alloc_text (PAGE, RawaccelControl)
#endif
-namespace ra = rawaccel;
-
using milliseconds = double;
-using lut_value_t = ra::si_pair;
struct {
ra::settings args;
- milliseconds tick_interval = 0; // set in DriverEntry
+ milliseconds tick_interval;
+ vec2<ra::accel_invoker> invokers;
ra::mouse_modifier modifier;
- vec2<lut_value_t*> lookups = {};
-} global;
+} global = {};
+extern "C" PULONG InitSafeBootMode;
+
+__declspec(guard(ignore))
VOID
RawaccelCallback(
IN PDEVICE_OBJECT DeviceObject,
@@ -56,52 +57,44 @@ Arguments:
auto num_packets = InputDataEnd - InputDataStart;
- bool any = num_packets > 0;
- bool rel_move = !(InputDataStart->Flags & MOUSE_MOVE_ABSOLUTE);
- bool dev_match = global.args.device_id[0] == 0 ||
- wcsncmp(devExt->dev_id, global.args.device_id, MAX_DEV_ID_LEN) == 0;
-
- if (any && rel_move && dev_match) {
- // if IO is backed up to the point where we get more than 1 packet here
- // then applying accel is pointless as we can't get an accurate timing
- bool enable_accel = num_packets == 1;
-
+ if (num_packets > 0 &&
+ !(InputDataStart->Flags & MOUSE_MOVE_ABSOLUTE) &&
+ (global.args.device_id[0] == 0 ||
+ bool(wcsncmp(devExt->dev_id, global.args.device_id, ra::MAX_DEV_ID_LEN)) ==
+ global.args.ignore)) {
+ counter_t now = KeQueryPerformanceCounter(NULL).QuadPart;
+ counter_t ticks = now - devExt->counter;
+ devExt->counter = now;
+ milliseconds raw_elapsed = ticks * global.tick_interval;
+ milliseconds time = ra::clampsd(raw_elapsed / num_packets,
+ global.args.time_min,
+ global.args.time_max);
auto it = InputDataStart;
do {
- vec2d input = {
- static_cast<double>(it->LastX),
- static_cast<double>(it->LastY)
- };
-
- global.modifier.apply_rotation(input);
- global.modifier.apply_angle_snap(input);
-
- if (enable_accel) {
- auto time_supplier = [=] {
- counter_t now = KeQueryPerformanceCounter(NULL).QuadPart;
- counter_t ticks = now - devExt->counter;
- devExt->counter = now;
- milliseconds time = ticks * global.tick_interval;
- return clampsd(time, global.args.time_min, 100);
+ if (it->LastX || it->LastY) {
+ vec2d input = {
+ static_cast<double>(it->LastX),
+ static_cast<double>(it->LastY)
};
- global.modifier.apply_acceleration(input, time_supplier);
- }
-
- global.modifier.apply_sensitivity(input);
-
- double carried_result_x = input.x + devExt->carry.x;
- double carried_result_y = input.y + devExt->carry.y;
+ global.modifier.modify(input, global.invokers, time);
- LONG out_x = static_cast<LONG>(carried_result_x);
- LONG out_y = static_cast<LONG>(carried_result_y);
+ double carried_result_x = input.x + devExt->carry.x;
+ double carried_result_y = input.y + devExt->carry.y;
- devExt->carry.x = carried_result_x - out_x;
- devExt->carry.y = carried_result_y - out_y;
+ LONG out_x = static_cast<LONG>(carried_result_x);
+ LONG out_y = static_cast<LONG>(carried_result_y);
- it->LastX = out_x;
- it->LastY = out_y;
+ double carry_x = carried_result_x - out_x;
+ double carry_y = carried_result_y - out_y;
+ if (!ra::infnan(carry_x + carry_y)) {
+ devExt->carry.x = carried_result_x - out_x;
+ devExt->carry.y = carried_result_y - out_y;
+ it->LastX = out_x;
+ it->LastY = out_y;
+ }
+ }
} while (++it != InputDataEnd);
}
@@ -160,7 +153,7 @@ Return Value:
case RA_READ:
status = WdfRequestRetrieveOutputBuffer(
Request,
- sizeof(ra::settings),
+ sizeof(ra::io_t),
&buffer,
NULL
);
@@ -168,14 +161,18 @@ Return Value:
DebugPrint(("RetrieveOutputBuffer failed: 0x%x\n", status));
}
else {
- *reinterpret_cast<ra::settings*>(buffer) = global.args;
- bytes_out = sizeof(ra::settings);
+ ra::io_t& output = *reinterpret_cast<ra::io_t*>(buffer);
+
+ output.args = global.args;
+ output.mod = global.modifier;
+
+ bytes_out = sizeof(ra::io_t);
}
break;
case RA_WRITE:
status = WdfRequestRetrieveInputBuffer(
Request,
- sizeof(ra::settings),
+ sizeof(ra::io_t),
&buffer,
NULL
);
@@ -187,14 +184,11 @@ Return Value:
interval.QuadPart = static_cast<LONGLONG>(ra::WRITE_DELAY) * -10000;
KeDelayExecutionThread(KernelMode, FALSE, &interval);
- ra::settings new_settings = *reinterpret_cast<ra::settings*>(buffer);
+ ra::io_t& input = *reinterpret_cast<ra::io_t*>(buffer);
- if (new_settings.time_min <= 0 || _isnanf(static_cast<float>(new_settings.time_min))) {
- new_settings.time_min = ra::settings{}.time_min;
- }
-
- global.args = new_settings;
- global.modifier = { global.args, global.lookups };
+ global.args = input.args;
+ global.invokers = ra::invokers(input.args);
+ global.modifier = input.mod;
}
break;
case RA_GET_VERSION:
@@ -208,7 +202,7 @@ Return Value:
DebugPrint(("RetrieveOutputBuffer failed: 0x%x\n", status));
}
else {
- *reinterpret_cast<ra::version_t*>(buffer) = { RA_VER_MAJOR, RA_VER_MINOR, RA_VER_PATCH };
+ *reinterpret_cast<ra::version_t*>(buffer) = ra::version;
bytes_out = sizeof(ra::version_t);
}
break;
@@ -272,23 +266,6 @@ Routine Description:
KeQueryPerformanceCounter(&freq);
global.tick_interval = 1e3 / freq.QuadPart;
- auto make_lut = [] {
- const size_t POOL_SIZE = sizeof(lut_value_t) * ra::LUT_SIZE;
-
- auto pool = ExAllocatePoolWithTag(NonPagedPool, POOL_SIZE, 'AR');
-
- if (!pool) {
- DebugPrint(("RA - failed to allocate LUT\n"));
- }
- else {
- RtlZeroMemory(pool, POOL_SIZE);
- }
-
- return reinterpret_cast<lut_value_t*>(pool);
- };
-
- global.lookups = { make_lut(), make_lut() };
-
CreateControlDevice(driver);
}
else {
@@ -453,13 +430,17 @@ Return Value:
NTSTATUS status;
WDFDEVICE hDevice;
WDF_IO_QUEUE_CONFIG ioQueueConfig;
-
+
UNREFERENCED_PARAMETER(Driver);
PAGED_CODE();
DebugPrint(("Enter FilterEvtDeviceAdd \n"));
+ if (*InitSafeBootMode > 0) {
+ return STATUS_SUCCESS;
+ }
+
//
// Tell the framework that you are filter driver. Framework
// takes care of inherting all the device flags & characterstics
@@ -507,7 +488,7 @@ Return Value:
if (NT_SUCCESS(nts)) {
auto* id_ptr = reinterpret_cast<WCHAR*>(iosb.Information);
- wcsncpy(FilterGetData(hDevice)->dev_id, id_ptr, MAX_DEV_ID_LEN);
+ wcsncpy(FilterGetData(hDevice)->dev_id, id_ptr, ra::MAX_DEV_ID_LEN);
DebugPrint(("Device ID = %ws\n", id_ptr));
ExFreePool(id_ptr);
}
diff --git a/driver/driver.h b/driver/driver.h
index a0381fb..6184a69 100644
--- a/driver/driver.h
+++ b/driver/driver.h
@@ -1,11 +1,11 @@
#pragma once
-#include <ntddk.h>
+#include "rawaccel-base.hpp"
+#include "rawaccel-io-def.h"
+
#include <kbdmou.h>
#include <wdf.h>
-#include "rawaccel-settings.h"
-
#if DBG
#define DebugPrint(_x_) DbgPrint _x_
#else
@@ -16,12 +16,13 @@
#define SYMBOLIC_NAME_STRING L"\\DosDevices\\rawaccel"
using counter_t = long long;
+namespace ra = rawaccel;
typedef struct _DEVICE_EXTENSION {
counter_t counter;
vec2d carry;
CONNECT_DATA UpperConnectData;
- WCHAR dev_id[MAX_DEV_ID_LEN];
+ WCHAR dev_id[ra::MAX_DEV_ID_LEN];
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_EXTENSION, FilterGetData)
diff --git a/driver/driver.vcxproj b/driver/driver.vcxproj
index 9034680..c2e4629 100644
--- a/driver/driver.vcxproj
+++ b/driver/driver.vcxproj
@@ -30,6 +30,7 @@
<ConfigurationType>Driver</ConfigurationType>
<OverrideDefaultRuntimeLibrary>true</OverrideDefaultRuntimeLibrary>
<SpectreMitigation>Spectre</SpectreMitigation>
+ <Driver_SpectreMitigation>Spectre</Driver_SpectreMitigation>
</PropertyGroup>
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<TargetVersion>Windows7</TargetVersion>
@@ -38,6 +39,7 @@
<DriverType>KMDF</DriverType>
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
<ConfigurationType>Driver</ConfigurationType>
+ <Driver_SpectreMitigation>Spectre</Driver_SpectreMitigation>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<PropertyGroup>
@@ -74,18 +76,18 @@
</RuntimeLibrary>
<TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
<CallingConvention>StdCall</CallingConvention>
- <AssemblerOutput>AssemblyAndSourceCode</AssemblerOutput>
+ <AssemblerOutput>AssemblyCode</AssemblerOutput>
<ExpandAttributedSource>false</ExpandAttributedSource>
<UseUnicodeForAssemblerListing>false</UseUnicodeForAssemblerListing>
<AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
<Optimization>MaxSpeed</Optimization>
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
<IntrinsicFunctions>true</IntrinsicFunctions>
- <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
<WholeProgramOptimization>false</WholeProgramOptimization>
<BufferSecurityCheck>true</BufferSecurityCheck>
<ControlFlowGuard>Guard</ControlFlowGuard>
- <FloatingPointModel>Precise</FloatingPointModel>
+ <FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<LinkTimeCodeGeneration>
@@ -114,9 +116,11 @@
<RuntimeLibrary>
</RuntimeLibrary>
<TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
- <FloatingPointModel>Precise</FloatingPointModel>
+ <FloatingPointModel>Fast</FloatingPointModel>
<CallingConvention>StdCall</CallingConvention>
<Optimization>MaxSpeed</Optimization>
+ <ControlFlowGuard>Guard</ControlFlowGuard>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
</ClCompile>
<Link>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
diff --git a/grapher/Constants/Constants.cs b/grapher/Constants/Constants.cs
index ce18389..b99f662 100644
--- a/grapher/Constants/Constants.cs
+++ b/grapher/Constants/Constants.cs
@@ -44,7 +44,7 @@ namespace grapher
public const int DropDownLeftSeparation = 10;
/// <summary> Height of sensitivity chart when displayed alone. </summary>
- public const int SensitivityChartAloneHeight = 455;
+ public const int SensitivityChartAloneHeight = 480;
/// <summary> Height of sensitivity chart when displayed alongside Velocity and Gain charts. </summary>
public const int SensitivityChartTogetherHeight = 328;
@@ -64,6 +64,9 @@ namespace grapher
/// <summary> Vertical placement of write button above bottom of sensitivity graph </summary>
public const int ButtonVerticalOffset = 60;
+ /// <summary> Vertical placement of directionality panel below top of containing form </summary>
+ public const int DirectionalityVerticalOffset = 285;
+
/// <summary> Padding between directionality title and containing panel </summary>
public const int DirectionalityTitlePad = 8;
@@ -120,6 +123,8 @@ namespace grapher
/// <summary> Default name of settings file. </summary>
public const string DefaultSettingsFileName = @"settings.json";
+ public const string GuiConfigFileName = ".config";
+
/// <summary> Text to direcitonality panel title when panel is closed. </summary>
public const string DirectionalityTitleClosed = "Anisotropy \u25BC";
diff --git a/grapher/Form1.Designer.cs b/grapher/Form1.Designer.cs
index 5c9180d..c114ff6 100644
--- a/grapher/Form1.Designer.cs
+++ b/grapher/Form1.Designer.cs
@@ -71,6 +71,20 @@ namespace grapher
System.Windows.Forms.DataVisualization.Charting.Title title6 = new System.Windows.Forms.DataVisualization.Charting.Title();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(RawAcceleration));
this.optionsPanel = new System.Windows.Forms.Panel();
+ this.LutApplyActiveYLabel = new System.Windows.Forms.Label();
+ this.YLutApplyDropdown = new System.Windows.Forms.ComboBox();
+ this.XLutApplyDropdown = new System.Windows.Forms.ComboBox();
+ this.LutApplyActiveXLabel = new System.Windows.Forms.Label();
+ this.YLutApplyLabel = new System.Windows.Forms.Label();
+ this.XLutApplyLabel = new System.Windows.Forms.Label();
+ this.YLutPointsBox = new System.Windows.Forms.RichTextBox();
+ this.XLutPointsBox = new System.Windows.Forms.RichTextBox();
+ this.gainSwitchActiveLabelY = new System.Windows.Forms.Label();
+ this.gainSwitchActiveLabelX = new System.Windows.Forms.Label();
+ this.gainSwitchY = new System.Windows.Forms.CheckBox();
+ this.gainSwitchX = new System.Windows.Forms.CheckBox();
+ this.LUTTextLabelY = new System.Windows.Forms.Label();
+ this.LUTTextLabelX = new System.Windows.Forms.Label();
this.FakeBox = new System.Windows.Forms.CheckBox();
this.DirectionalityPanel = new System.Windows.Forms.Panel();
this.LpNormActiveValue = new System.Windows.Forms.Label();
@@ -173,12 +187,6 @@ namespace grapher
this.showVelocityGainToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.showLastMouseMoveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.advancedToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.capStyleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.gainCapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.legacyCapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.offsetStyleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.gainOffsetToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
- this.legacyOffsetToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.AutoWriteMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.UseSpecificDeviceMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.chartsPanel = new System.Windows.Forms.Panel();
@@ -189,6 +197,8 @@ namespace grapher
this.VelocityChart = new System.Windows.Forms.DataVisualization.Charting.Chart();
this.AccelerationChart = new System.Windows.Forms.DataVisualization.Charting.Chart();
this.streamingModeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.XLutActiveValuesBox = new System.Windows.Forms.RichTextBox();
+ this.YLutActiveValuesBox = new System.Windows.Forms.RichTextBox();
this.optionsPanel.SuspendLayout();
this.DirectionalityPanel.SuspendLayout();
this.menuStrip1.SuspendLayout();
@@ -204,6 +214,22 @@ namespace grapher
// optionsPanel
//
this.optionsPanel.AutoSize = true;
+ this.optionsPanel.Controls.Add(this.YLutActiveValuesBox);
+ this.optionsPanel.Controls.Add(this.XLutActiveValuesBox);
+ this.optionsPanel.Controls.Add(this.LutApplyActiveYLabel);
+ this.optionsPanel.Controls.Add(this.YLutApplyDropdown);
+ this.optionsPanel.Controls.Add(this.XLutApplyDropdown);
+ this.optionsPanel.Controls.Add(this.LutApplyActiveXLabel);
+ this.optionsPanel.Controls.Add(this.YLutApplyLabel);
+ this.optionsPanel.Controls.Add(this.XLutApplyLabel);
+ this.optionsPanel.Controls.Add(this.YLutPointsBox);
+ this.optionsPanel.Controls.Add(this.XLutPointsBox);
+ this.optionsPanel.Controls.Add(this.gainSwitchActiveLabelY);
+ this.optionsPanel.Controls.Add(this.gainSwitchActiveLabelX);
+ this.optionsPanel.Controls.Add(this.gainSwitchY);
+ this.optionsPanel.Controls.Add(this.gainSwitchX);
+ this.optionsPanel.Controls.Add(this.LUTTextLabelY);
+ this.optionsPanel.Controls.Add(this.LUTTextLabelX);
this.optionsPanel.Controls.Add(this.FakeBox);
this.optionsPanel.Controls.Add(this.DirectionalityPanel);
this.optionsPanel.Controls.Add(this.toggleButton);
@@ -284,6 +310,134 @@ namespace grapher
this.optionsPanel.Size = new System.Drawing.Size(483, 956);
this.optionsPanel.TabIndex = 34;
//
+ // LutApplyActiveYLabel
+ //
+ this.LutApplyActiveYLabel.AutoSize = true;
+ this.LutApplyActiveYLabel.Location = new System.Drawing.Point(417, 565);
+ this.LutApplyActiveYLabel.Name = "LutApplyActiveYLabel";
+ this.LutApplyActiveYLabel.Size = new System.Drawing.Size(35, 13);
+ this.LutApplyActiveYLabel.TabIndex = 167;
+ this.LutApplyActiveYLabel.Text = "label1";
+ //
+ // YLutApplyDropdown
+ //
+ this.YLutApplyDropdown.FormattingEnabled = true;
+ this.YLutApplyDropdown.Location = new System.Drawing.Point(397, 527);
+ this.YLutApplyDropdown.Name = "YLutApplyDropdown";
+ this.YLutApplyDropdown.Size = new System.Drawing.Size(73, 21);
+ this.YLutApplyDropdown.TabIndex = 166;
+ //
+ // XLutApplyDropdown
+ //
+ this.XLutApplyDropdown.FormattingEnabled = true;
+ this.XLutApplyDropdown.Location = new System.Drawing.Point(320, 527);
+ this.XLutApplyDropdown.Name = "XLutApplyDropdown";
+ this.XLutApplyDropdown.Size = new System.Drawing.Size(65, 21);
+ this.XLutApplyDropdown.TabIndex = 165;
+ //
+ // LutApplyActiveXLabel
+ //
+ this.LutApplyActiveXLabel.AutoSize = true;
+ this.LutApplyActiveXLabel.Location = new System.Drawing.Point(200, 565);
+ this.LutApplyActiveXLabel.Name = "LutApplyActiveXLabel";
+ this.LutApplyActiveXLabel.Size = new System.Drawing.Size(35, 13);
+ this.LutApplyActiveXLabel.TabIndex = 164;
+ this.LutApplyActiveXLabel.Text = "label1";
+ //
+ // YLutApplyLabel
+ //
+ this.YLutApplyLabel.AutoSize = true;
+ this.YLutApplyLabel.Location = new System.Drawing.Point(397, 510);
+ this.YLutApplyLabel.Name = "YLutApplyLabel";
+ this.YLutApplyLabel.Size = new System.Drawing.Size(47, 13);
+ this.YLutApplyLabel.TabIndex = 163;
+ this.YLutApplyLabel.Text = "Apply as";
+ //
+ // XLutApplyLabel
+ //
+ this.XLutApplyLabel.AutoSize = true;
+ this.XLutApplyLabel.Location = new System.Drawing.Point(317, 511);
+ this.XLutApplyLabel.Name = "XLutApplyLabel";
+ this.XLutApplyLabel.Size = new System.Drawing.Size(47, 13);
+ this.XLutApplyLabel.TabIndex = 162;
+ this.XLutApplyLabel.Text = "Apply as";
+ //
+ // YLutPointsBox
+ //
+ this.YLutPointsBox.Location = new System.Drawing.Point(397, 447);
+ this.YLutPointsBox.Name = "YLutPointsBox";
+ this.YLutPointsBox.Size = new System.Drawing.Size(73, 57);
+ this.YLutPointsBox.TabIndex = 161;
+ this.YLutPointsBox.Text = "";
+ //
+ // XLutPointsBox
+ //
+ this.XLutPointsBox.Location = new System.Drawing.Point(317, 447);
+ this.XLutPointsBox.Name = "XLutPointsBox";
+ this.XLutPointsBox.Size = new System.Drawing.Size(68, 57);
+ this.XLutPointsBox.TabIndex = 160;
+ this.XLutPointsBox.Text = "";
+ //
+ // gainSwitchActiveLabelY
+ //
+ this.gainSwitchActiveLabelY.AutoSize = true;
+ this.gainSwitchActiveLabelY.Location = new System.Drawing.Point(417, 95);
+ this.gainSwitchActiveLabelY.Name = "gainSwitchActiveLabelY";
+ this.gainSwitchActiveLabelY.Size = new System.Drawing.Size(29, 13);
+ this.gainSwitchActiveLabelY.TabIndex = 157;
+ this.gainSwitchActiveLabelY.Text = "Gain";
+ //
+ // gainSwitchActiveLabelX
+ //
+ this.gainSwitchActiveLabelX.AutoSize = true;
+ this.gainSwitchActiveLabelX.Location = new System.Drawing.Point(200, 96);
+ this.gainSwitchActiveLabelX.Name = "gainSwitchActiveLabelX";
+ this.gainSwitchActiveLabelX.Size = new System.Drawing.Size(29, 13);
+ this.gainSwitchActiveLabelX.TabIndex = 156;
+ this.gainSwitchActiveLabelX.Text = "Gain";
+ //
+ // gainSwitchY
+ //
+ this.gainSwitchY.AutoSize = true;
+ this.gainSwitchY.Checked = true;
+ this.gainSwitchY.CheckState = System.Windows.Forms.CheckState.Checked;
+ this.gainSwitchY.Location = new System.Drawing.Point(283, 116);
+ this.gainSwitchY.Name = "gainSwitchY";
+ this.gainSwitchY.Size = new System.Drawing.Size(48, 17);
+ this.gainSwitchY.TabIndex = 155;
+ this.gainSwitchY.Text = "Gain";
+ this.gainSwitchY.UseVisualStyleBackColor = true;
+ //
+ // gainSwitchX
+ //
+ this.gainSwitchX.AutoSize = true;
+ this.gainSwitchX.Checked = true;
+ this.gainSwitchX.CheckState = System.Windows.Forms.CheckState.Checked;
+ this.gainSwitchX.Location = new System.Drawing.Point(38, 116);
+ this.gainSwitchX.Name = "gainSwitchX";
+ this.gainSwitchX.Size = new System.Drawing.Size(48, 17);
+ this.gainSwitchX.TabIndex = 154;
+ this.gainSwitchX.Text = "Gain";
+ this.gainSwitchX.UseVisualStyleBackColor = true;
+ //
+ // LUTTextLabelY
+ //
+ this.LUTTextLabelY.AutoSize = true;
+ this.LUTTextLabelY.Location = new System.Drawing.Point(266, 350);
+ this.LUTTextLabelY.Name = "LUTTextLabelY";
+ this.LUTTextLabelY.Size = new System.Drawing.Size(52, 13);
+ this.LUTTextLabelY.TabIndex = 153;
+ this.LUTTextLabelY.Text = "LUT Text";
+ //
+ // LUTTextLabelX
+ //
+ this.LUTTextLabelX.AutoSize = true;
+ this.LUTTextLabelX.Location = new System.Drawing.Point(38, 350);
+ this.LUTTextLabelX.Name = "LUTTextLabelX";
+ this.LUTTextLabelX.Size = new System.Drawing.Size(52, 13);
+ this.LUTTextLabelX.TabIndex = 152;
+ this.LUTTextLabelX.Text = "LUT Text";
+ //
// FakeBox
//
this.FakeBox.AutoSize = true;
@@ -1178,58 +1332,12 @@ namespace grapher
// advancedToolStripMenuItem
//
this.advancedToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
- this.capStyleToolStripMenuItem,
- this.offsetStyleToolStripMenuItem,
this.AutoWriteMenuItem,
this.UseSpecificDeviceMenuItem});
this.advancedToolStripMenuItem.Name = "advancedToolStripMenuItem";
this.advancedToolStripMenuItem.Size = new System.Drawing.Size(72, 20);
this.advancedToolStripMenuItem.Text = "Advanced";
//
- // capStyleToolStripMenuItem
- //
- this.capStyleToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
- this.gainCapToolStripMenuItem,
- this.legacyCapToolStripMenuItem});
- this.capStyleToolStripMenuItem.Name = "capStyleToolStripMenuItem";
- this.capStyleToolStripMenuItem.Size = new System.Drawing.Size(210, 22);
- this.capStyleToolStripMenuItem.Text = "Cap Style";
- //
- // gainCapToolStripMenuItem
- //
- this.gainCapToolStripMenuItem.Checked = true;
- this.gainCapToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked;
- this.gainCapToolStripMenuItem.Name = "gainCapToolStripMenuItem";
- this.gainCapToolStripMenuItem.Size = new System.Drawing.Size(147, 22);
- this.gainCapToolStripMenuItem.Text = "Gain (Default)";
- //
- // legacyCapToolStripMenuItem
- //
- this.legacyCapToolStripMenuItem.Name = "legacyCapToolStripMenuItem";
- this.legacyCapToolStripMenuItem.Size = new System.Drawing.Size(147, 22);
- this.legacyCapToolStripMenuItem.Text = "Legacy";
- //
- // offsetStyleToolStripMenuItem
- //
- this.offsetStyleToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
- this.gainOffsetToolStripMenuItem,
- this.legacyOffsetToolStripMenuItem});
- this.offsetStyleToolStripMenuItem.Name = "offsetStyleToolStripMenuItem";
- this.offsetStyleToolStripMenuItem.Size = new System.Drawing.Size(210, 22);
- this.offsetStyleToolStripMenuItem.Text = "Offset Style";
- //
- // gainOffsetToolStripMenuItem
- //
- this.gainOffsetToolStripMenuItem.Name = "gainOffsetToolStripMenuItem";
- this.gainOffsetToolStripMenuItem.Size = new System.Drawing.Size(147, 22);
- this.gainOffsetToolStripMenuItem.Text = "Gain (Default)";
- //
- // legacyOffsetToolStripMenuItem
- //
- this.legacyOffsetToolStripMenuItem.Name = "legacyOffsetToolStripMenuItem";
- this.legacyOffsetToolStripMenuItem.Size = new System.Drawing.Size(147, 22);
- this.legacyOffsetToolStripMenuItem.Text = "Legacy";
- //
// AutoWriteMenuItem
//
this.AutoWriteMenuItem.Checked = true;
@@ -1504,6 +1612,22 @@ namespace grapher
this.streamingModeToolStripMenuItem.Size = new System.Drawing.Size(201, 22);
this.streamingModeToolStripMenuItem.Text = "Streaming Mode";
//
+ // XLutActiveValuesBox
+ //
+ this.XLutActiveValuesBox.Location = new System.Drawing.Point(317, 369);
+ this.XLutActiveValuesBox.Name = "XLutActiveValuesBox";
+ this.XLutActiveValuesBox.Size = new System.Drawing.Size(68, 72);
+ this.XLutActiveValuesBox.TabIndex = 168;
+ this.XLutActiveValuesBox.Text = "";
+ //
+ // YLutActiveValuesBox
+ //
+ this.YLutActiveValuesBox.Location = new System.Drawing.Point(397, 369);
+ this.YLutActiveValuesBox.Name = "YLutActiveValuesBox";
+ this.YLutActiveValuesBox.Size = new System.Drawing.Size(73, 72);
+ this.YLutActiveValuesBox.TabIndex = 169;
+ this.YLutActiveValuesBox.Text = "";
+ //
// RawAcceleration
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -1615,12 +1739,6 @@ namespace grapher
private System.Windows.Forms.ToolStripMenuItem showVelocityGainToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem showLastMouseMoveToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem advancedToolStripMenuItem;
- private System.Windows.Forms.ToolStripMenuItem capStyleToolStripMenuItem;
- private System.Windows.Forms.ToolStripMenuItem gainCapToolStripMenuItem;
- private System.Windows.Forms.ToolStripMenuItem legacyCapToolStripMenuItem;
- private System.Windows.Forms.ToolStripMenuItem offsetStyleToolStripMenuItem;
- private System.Windows.Forms.ToolStripMenuItem gainOffsetToolStripMenuItem;
- private System.Windows.Forms.ToolStripMenuItem legacyOffsetToolStripMenuItem;
private System.Windows.Forms.Panel chartsPanel;
private System.Windows.Forms.DataVisualization.Charting.Chart GainChartY;
private System.Windows.Forms.DataVisualization.Charting.Chart VelocityChartY;
@@ -1651,6 +1769,22 @@ namespace grapher
private System.Windows.Forms.Label RangeActiveValueY;
private System.Windows.Forms.CheckBox FakeBox;
private System.Windows.Forms.ToolStripMenuItem UseSpecificDeviceMenuItem;
+ private System.Windows.Forms.Label LUTTextLabelX;
+ private System.Windows.Forms.Label LUTTextLabelY;
+ private System.Windows.Forms.CheckBox gainSwitchX;
+ private System.Windows.Forms.CheckBox gainSwitchY;
+ private System.Windows.Forms.Label gainSwitchActiveLabelY;
+ private System.Windows.Forms.Label gainSwitchActiveLabelX;
+ private System.Windows.Forms.RichTextBox YLutPointsBox;
+ private System.Windows.Forms.RichTextBox XLutPointsBox;
+ private System.Windows.Forms.Label LutApplyActiveYLabel;
+ private System.Windows.Forms.ComboBox YLutApplyDropdown;
+ private System.Windows.Forms.ComboBox XLutApplyDropdown;
+ private System.Windows.Forms.Label LutApplyActiveXLabel;
+ private System.Windows.Forms.Label YLutApplyLabel;
+ private System.Windows.Forms.Label XLutApplyLabel;
+ private System.Windows.Forms.RichTextBox YLutActiveValuesBox;
+ private System.Windows.Forms.RichTextBox XLutActiveValuesBox;
private System.Windows.Forms.ToolStripMenuItem streamingModeToolStripMenuItem;
}
}
diff --git a/grapher/Form1.cs b/grapher/Form1.cs
index d833311..3971322 100644
--- a/grapher/Form1.cs
+++ b/grapher/Form1.cs
@@ -28,14 +28,13 @@ namespace grapher
{
InitializeComponent();
- Version assemVersion = typeof(RawAcceleration).Assembly.GetName().Version;
Version driverVersion = null;
try
{
- driverVersion = VersionHelper.ValidateAndGetDriverVersion(assemVersion);
+ driverVersion = VersionHelper.ValidOrThrow();
}
- catch (VersionException ex)
+ catch (InteropException ex)
{
MessageBox.Show(ex.Message);
throw;
@@ -51,7 +50,7 @@ namespace grapher
AccelGUI = AccelGUIFactory.Construct(
this,
- ManagedAccel.GetActiveAccel(),
+ ManagedAccel.GetActive(),
AccelerationChart,
AccelerationChartY,
VelocityChart,
@@ -60,15 +59,13 @@ namespace grapher
GainChartY,
accelTypeDropX,
accelTypeDropY,
+ XLutApplyDropdown,
+ YLutApplyDropdown,
writeButton,
toggleButton,
showVelocityGainToolStripMenuItem,
showLastMouseMoveToolStripMenuItem,
streamingModeToolStripMenuItem,
- gainCapToolStripMenuItem,
- legacyCapToolStripMenuItem,
- gainOffsetToolStripMenuItem,
- legacyOffsetToolStripMenuItem,
AutoWriteMenuItem,
UseSpecificDeviceMenuItem,
ScaleMenuItem,
@@ -104,6 +101,12 @@ namespace grapher
FakeBox,
WholeCheckBox,
ByComponentCheckBox,
+ gainSwitchX,
+ gainSwitchY,
+ XLutActiveValuesBox,
+ YLutActiveValuesBox,
+ XLutPointsBox,
+ YLutPointsBox,
LockXYLabel,
sensitivityLabel,
rotationLabel,
@@ -121,6 +124,8 @@ namespace grapher
limitLabelY,
expLabelX,
expLabelY,
+ LUTTextLabelX,
+ LUTTextLabelY,
constantThreeLabelX,
constantThreeLabelY,
ActiveValueTitle,
@@ -146,6 +151,8 @@ namespace grapher
MidpointActiveYLabel,
AccelTypeActiveLabelX,
AccelTypeActiveLabelY,
+ gainSwitchActiveLabelX,
+ gainSwitchActiveLabelY,
OptionSetXTitle,
OptionSetYTitle,
MouseLabel,
@@ -160,7 +167,11 @@ namespace grapher
DomainActiveValueY,
DirectionalityRangeLabel,
RangeActiveValueX,
- RangeActiveValueY);
+ RangeActiveValueY,
+ XLutApplyLabel,
+ YLutApplyLabel,
+ LutApplyActiveXLabel,
+ LutApplyActiveYLabel);
ResizeAndCenter();
}
diff --git a/grapher/Layouts/ClassicLayout.cs b/grapher/Layouts/ClassicLayout.cs
index 8403c5d..ca9195e 100644
--- a/grapher/Layouts/ClassicLayout.cs
+++ b/grapher/Layouts/ClassicLayout.cs
@@ -8,8 +8,9 @@ namespace grapher.Layouts
: base()
{
Name = "Classic";
- Index = (int)AccelMode.classic;
+ Mode = AccelMode.classic;
+ GainSwitchOptionLayout = new OptionLayout(true, Gain);
AccelLayout = new OptionLayout(true, Acceleration);
ScaleLayout = new OptionLayout(false, string.Empty);
CapLayout = new OptionLayout(true, Cap);
@@ -18,6 +19,8 @@ namespace grapher.Layouts
LimitLayout = new OptionLayout(false, string.Empty);
ExponentLayout = new OptionLayout(true, Exponent);
MidpointLayout = new OptionLayout(false, string.Empty);
+ LutTextLayout = new OptionLayout(false, string.Empty);
+ LutPanelLayout = new OptionLayout(false, string.Empty);
}
}
}
diff --git a/grapher/Layouts/DefaultLayout.cs b/grapher/Layouts/DefaultLayout.cs
index c2f7fd7..055536a 100644
--- a/grapher/Layouts/DefaultLayout.cs
+++ b/grapher/Layouts/DefaultLayout.cs
@@ -8,9 +8,10 @@ namespace grapher.Layouts
: base()
{
Name = "Default";
- Index = (int)AccelMode.noaccel;
+ Mode = AccelMode.noaccel;
LogarithmicCharts = false;
+ GainSwitchOptionLayout = new OptionLayout(true, Gain);
AccelLayout = new OptionLayout(true, Acceleration);
ScaleLayout = new OptionLayout(true, Scale);
CapLayout = new OptionLayout(true, Cap);
@@ -19,6 +20,8 @@ namespace grapher.Layouts
LimitLayout = new OptionLayout(true, Limit);
ExponentLayout = new OptionLayout(true, Exponent);
MidpointLayout = new OptionLayout(true, Midpoint);
+ LutTextLayout = new OptionLayout(false, string.Empty);
+ LutPanelLayout = new OptionLayout(false, string.Empty);
}
}
}
diff --git a/grapher/Layouts/JumpLayout.cs b/grapher/Layouts/JumpLayout.cs
new file mode 100644
index 0000000..4e34ef3
--- /dev/null
+++ b/grapher/Layouts/JumpLayout.cs
@@ -0,0 +1,27 @@
+using grapher.Models.Serialized;
+
+namespace grapher.Layouts
+{
+ public class JumpLayout : LayoutBase
+ {
+ public JumpLayout()
+ : base()
+ {
+ Name = "Jump";
+ Mode = AccelMode.jump;
+ LogarithmicCharts = false;
+
+ GainSwitchOptionLayout = new OptionLayout(true, Gain);
+ AccelLayout = new OptionLayout(true, Smooth);
+ ScaleLayout = new OptionLayout(false, string.Empty);
+ CapLayout = new OptionLayout(true, Cap);
+ WeightLayout = new OptionLayout(false, Weight);
+ OffsetLayout = new OptionLayout(true, Offset);
+ LimitLayout = new OptionLayout(false, Limit);
+ ExponentLayout = new OptionLayout(false, string.Empty);
+ MidpointLayout = new OptionLayout(false, string.Empty);
+ LutTextLayout = new OptionLayout(false, string.Empty);
+ LutPanelLayout = new OptionLayout(false, string.Empty);
+ }
+ }
+}
diff --git a/grapher/Layouts/LUTLayout.cs b/grapher/Layouts/LUTLayout.cs
new file mode 100644
index 0000000..1e0037f
--- /dev/null
+++ b/grapher/Layouts/LUTLayout.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace grapher.Layouts
+{
+ public class LUTLayout : LayoutBase
+ {
+ /// <summary>
+ /// String small enough to fit in active value label
+ /// </summary>
+ public const string LUTActiveName = "LUT";
+
+ public LUTLayout()
+ : base()
+ {
+ Name = "LookUpTable";
+ Mode = AccelMode.lut;
+
+ GainSwitchOptionLayout = new OptionLayout(false, string.Empty);
+ AccelLayout = new OptionLayout(false, Acceleration);
+ ScaleLayout = new OptionLayout(false, string.Empty);
+ CapLayout = new OptionLayout(false, Cap);
+ WeightLayout = new OptionLayout(false, Weight);
+ OffsetLayout = new OptionLayout(false, Offset);
+ LimitLayout = new OptionLayout(false, string.Empty);
+ ExponentLayout = new OptionLayout(false, Exponent);
+ MidpointLayout = new OptionLayout(false, string.Empty);
+ LutTextLayout = new OptionLayout(true, string.Empty);
+ LutPanelLayout = new OptionLayout(true, string.Empty);
+ LutApplyOptionsLayout = new OptionLayout(true, string.Empty);
+ }
+
+ public override string ActiveName => LUTActiveName;
+ }
+}
diff --git a/grapher/Layouts/LayoutBase.cs b/grapher/Layouts/LayoutBase.cs
index 83af292..db914dc 100644
--- a/grapher/Layouts/LayoutBase.cs
+++ b/grapher/Layouts/LayoutBase.cs
@@ -1,5 +1,4 @@
using grapher.Models.Options;
-using System.Windows.Forms;
namespace grapher.Layouts
{
@@ -14,6 +13,10 @@ namespace grapher.Layouts
public const string Offset = "Offset";
public const string Cap = "Cap";
public const string Weight = "Weight";
+ public const string Smooth = "Smooth";
+ public const string Gain = "Gain";
+ public const string GrowthRate = "Growth Rate";
+ public const string DecayRate = "Decay Rate";
public LayoutBase()
{
@@ -25,18 +28,20 @@ namespace grapher.Layouts
LimitLayout = new OptionLayout(false, string.Empty);
ExponentLayout = new OptionLayout(false, string.Empty);
MidpointLayout = new OptionLayout(false, string.Empty);
+ LutTextLayout = new OptionLayout(false, string.Empty);
+ LutPanelLayout = new OptionLayout(false, string.Empty);
+ LutApplyOptionsLayout = new OptionLayout(false, string.Empty);
+ GainSwitchOptionLayout = new OptionLayout(false, string.Empty);
LogarithmicCharts = false;
}
- /// <summary>
- /// Gets or sets mapping from acceleration type to identifying integer.
- /// Must match accel_mode defined in rawaccel-settings.h
- /// </summary>
- public int Index { get; protected set; }
+ public AccelMode Mode { get; protected set; }
public string Name { get; protected set; }
+ public virtual string ActiveName { get => Name; }
+
public bool LogarithmicCharts { get; protected set; }
protected OptionLayout AccelLayout { get; set; }
@@ -55,7 +60,21 @@ namespace grapher.Layouts
protected OptionLayout MidpointLayout { get; set; }
+ protected OptionLayout LutTextLayout { get; set; }
+
+ protected OptionLayout LutPanelLayout { get; set; }
+
+ protected OptionLayout LutApplyOptionsLayout { get; set; }
+
+ protected OptionLayout GainSwitchOptionLayout { get; set; }
+
+ public override string ToString()
+ {
+ return Name;
+ }
+
public void Layout(
+ IOption gainSwitchOption,
IOption accelOption,
IOption scaleOption,
IOption capOption,
@@ -64,12 +83,16 @@ namespace grapher.Layouts
IOption limitOption,
IOption expOption,
IOption midpointOption,
+ IOption lutTextOption,
+ IOption lutPanelOption,
+ IOption lutApplyOption,
int top)
{
IOption previous = null;
foreach (var option in new (OptionLayout, IOption)[] {
+ (GainSwitchOptionLayout, gainSwitchOption),
(AccelLayout, accelOption),
(ScaleLayout, scaleOption),
(CapLayout, capOption),
@@ -77,7 +100,10 @@ namespace grapher.Layouts
(OffsetLayout, offsetOption),
(LimitLayout, limitOption),
(ExponentLayout, expOption),
- (MidpointLayout, midpointOption)})
+ (MidpointLayout, midpointOption),
+ (LutTextLayout, lutTextOption),
+ (LutPanelLayout, lutPanelOption),
+ (LutApplyOptionsLayout, lutApplyOption)})
{
option.Item1.Layout(option.Item2);
@@ -98,6 +124,7 @@ namespace grapher.Layouts
}
public void Layout(
+ IOption gainSwitchOption,
IOption accelOption,
IOption scaleOption,
IOption capOption,
@@ -105,9 +132,13 @@ namespace grapher.Layouts
IOption offsetOption,
IOption limitOption,
IOption expOption,
- IOption midpointOption)
+ IOption midpointOption,
+ IOption lutTextOption,
+ IOption lutPanelOption,
+ IOption lutApplyOption)
{
- Layout(accelOption,
+ Layout(gainSwitchOption,
+ accelOption,
scaleOption,
capOption,
weightOption,
@@ -115,6 +146,9 @@ namespace grapher.Layouts
limitOption,
expOption,
midpointOption,
+ lutTextOption,
+ lutPanelOption,
+ lutApplyOption,
accelOption.Top);
}
}
diff --git a/grapher/Layouts/LinearLayout.cs b/grapher/Layouts/LinearLayout.cs
index 0412a2a..6447833 100644
--- a/grapher/Layouts/LinearLayout.cs
+++ b/grapher/Layouts/LinearLayout.cs
@@ -4,21 +4,26 @@ namespace grapher.Layouts
{
public class LinearLayout : LayoutBase
{
+ public const string LinearName = "Linear";
+
public LinearLayout()
: base()
{
- Name = "Linear";
- Index = (int)AccelMode.linear;
+ Name = LinearName;
+ Mode = AccelMode.classic;
LogarithmicCharts = false;
+ GainSwitchOptionLayout = new OptionLayout(true, Gain);
AccelLayout = new OptionLayout(true, Acceleration);
ScaleLayout = new OptionLayout(false, string.Empty);
CapLayout = new OptionLayout(true, Cap);
- WeightLayout = new OptionLayout(true, Weight);
+ WeightLayout = new OptionLayout(false, Weight);
OffsetLayout = new OptionLayout(true, Offset);
LimitLayout = new OptionLayout(false, string.Empty);
ExponentLayout = new OptionLayout(false, string.Empty);
MidpointLayout = new OptionLayout(false, string.Empty);
+ LutTextLayout = new OptionLayout(false, string.Empty);
+ LutPanelLayout = new OptionLayout(false, string.Empty);
}
}
}
diff --git a/grapher/Layouts/MotivityLayout.cs b/grapher/Layouts/MotivityLayout.cs
index b06e4fc..7ab2f3f 100644
--- a/grapher/Layouts/MotivityLayout.cs
+++ b/grapher/Layouts/MotivityLayout.cs
@@ -13,10 +13,11 @@ namespace grapher.Layouts
: base()
{
Name = "Motivity";
- Index = (int)AccelMode.motivity;
+ Mode = AccelMode.motivity;
LogarithmicCharts = true;
- AccelLayout = new OptionLayout(true, Acceleration);
+ GainSwitchOptionLayout = new OptionLayout(true, Gain);
+ AccelLayout = new OptionLayout(true, GrowthRate);
ScaleLayout = new OptionLayout(false, string.Empty);
CapLayout = new OptionLayout(false, string.Empty);
WeightLayout = new OptionLayout(false, string.Empty);
@@ -24,6 +25,8 @@ namespace grapher.Layouts
LimitLayout = new OptionLayout(true, Motivity);
ExponentLayout = new OptionLayout(false, string.Empty);
MidpointLayout = new OptionLayout(true, Midpoint);
+ LutTextLayout = new OptionLayout(false, string.Empty);
+ LutPanelLayout = new OptionLayout(false, string.Empty);
}
}
}
diff --git a/grapher/Layouts/NaturalGainLayout.cs b/grapher/Layouts/NaturalGainLayout.cs
deleted file mode 100644
index 12daed3..0000000
--- a/grapher/Layouts/NaturalGainLayout.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using grapher.Models.Serialized;
-
-namespace grapher.Layouts
-{
- public class NaturalGainLayout : LayoutBase
- {
- public NaturalGainLayout()
- : base()
- {
- Name = "NaturalGain";
- Index = (int)AccelMode.naturalgain;
- LogarithmicCharts = false;
-
- AccelLayout = new OptionLayout(true, Acceleration);
- ScaleLayout = new OptionLayout(false, string.Empty);
- CapLayout = new OptionLayout(false, string.Empty);
- WeightLayout = new OptionLayout(true, Weight);
- OffsetLayout = new OptionLayout(true, Offset);
- LimitLayout = new OptionLayout(true, Limit);
- ExponentLayout = new OptionLayout(false, string.Empty);
- MidpointLayout = new OptionLayout(false, string.Empty);
- }
- }
-}
diff --git a/grapher/Layouts/NaturalLayout.cs b/grapher/Layouts/NaturalLayout.cs
index 44129f9..245a37b 100644
--- a/grapher/Layouts/NaturalLayout.cs
+++ b/grapher/Layouts/NaturalLayout.cs
@@ -8,10 +8,11 @@ namespace grapher.Layouts
: base()
{
Name = "Natural";
- Index = (int)AccelMode.natural;
+ Mode = AccelMode.natural;
LogarithmicCharts = false;
- AccelLayout = new OptionLayout(true, Acceleration);
+ GainSwitchOptionLayout = new OptionLayout(true, Gain);
+ AccelLayout = new OptionLayout(true, DecayRate);
ScaleLayout = new OptionLayout(false, string.Empty);
CapLayout = new OptionLayout(false, string.Empty);
WeightLayout = new OptionLayout(true, Weight);
@@ -19,6 +20,8 @@ namespace grapher.Layouts
LimitLayout = new OptionLayout(true, Limit);
ExponentLayout = new OptionLayout(false, string.Empty);
MidpointLayout = new OptionLayout(false, string.Empty);
+ LutTextLayout = new OptionLayout(false, string.Empty);
+ LutPanelLayout = new OptionLayout(false, string.Empty);
}
}
}
diff --git a/grapher/Layouts/OffLayout.cs b/grapher/Layouts/OffLayout.cs
index 0b54cbb..16e5c19 100644
--- a/grapher/Layouts/OffLayout.cs
+++ b/grapher/Layouts/OffLayout.cs
@@ -8,9 +8,10 @@ namespace grapher.Layouts
: base()
{
Name = "Off";
- Index = (int)AccelMode.noaccel;
+ Mode = AccelMode.noaccel;
LogarithmicCharts = false;
+ GainSwitchOptionLayout = new OptionLayout(false, string.Empty);
AccelLayout = new OptionLayout(false, string.Empty);
ScaleLayout = new OptionLayout(false, string.Empty);
CapLayout = new OptionLayout(false, string.Empty);
@@ -19,6 +20,8 @@ namespace grapher.Layouts
LimitLayout = new OptionLayout(false, string.Empty);
ExponentLayout = new OptionLayout(false, string.Empty);
MidpointLayout = new OptionLayout(false, string.Empty);
+ LutTextLayout = new OptionLayout(false, string.Empty);
+ LutPanelLayout = new OptionLayout(false, string.Empty);
}
}
}
diff --git a/grapher/Layouts/PowerLayout.cs b/grapher/Layouts/PowerLayout.cs
index da7d5bb..bf40c24 100644
--- a/grapher/Layouts/PowerLayout.cs
+++ b/grapher/Layouts/PowerLayout.cs
@@ -1,6 +1,4 @@
-using grapher.Models.Serialized;
-
-namespace grapher.Layouts
+namespace grapher.Layouts
{
public class PowerLayout : LayoutBase
{
@@ -8,9 +6,10 @@ namespace grapher.Layouts
: base()
{
Name = "Power";
- Index = (int)AccelMode.power;
+ Mode = AccelMode.power;
LogarithmicCharts = false;
+ GainSwitchOptionLayout = new OptionLayout(true, Gain);
AccelLayout = new OptionLayout(false, string.Empty);
ScaleLayout = new OptionLayout(true, Scale);
CapLayout = new OptionLayout(true, Cap);
@@ -19,6 +18,8 @@ namespace grapher.Layouts
LimitLayout = new OptionLayout(false, string.Empty);
ExponentLayout = new OptionLayout(true, Exponent);
MidpointLayout = new OptionLayout(false, string.Empty);
+ LutTextLayout = new OptionLayout(false, string.Empty);
+ LutPanelLayout = new OptionLayout(false, string.Empty);
}
}
}
diff --git a/grapher/Layouts/UnsupportedLayout.cs b/grapher/Layouts/UnsupportedLayout.cs
new file mode 100644
index 0000000..c17812d
--- /dev/null
+++ b/grapher/Layouts/UnsupportedLayout.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace grapher.Layouts
+{
+ public class UnsupportedLayout : LayoutBase
+ {
+ public const string LUTLayoutText = "This mode is unsupported by this program. See the guide for details.";
+
+ public UnsupportedLayout()
+ : base()
+ {
+ Name = "Unsupported";
+ Mode = AccelMode.noaccel + 1;
+
+ GainSwitchOptionLayout = new OptionLayout(false, string.Empty);
+ AccelLayout = new OptionLayout(false, Acceleration);
+ ScaleLayout = new OptionLayout(false, string.Empty);
+ CapLayout = new OptionLayout(false, Cap);
+ WeightLayout = new OptionLayout(false, Weight);
+ OffsetLayout = new OptionLayout(false, Offset);
+ LimitLayout = new OptionLayout(false, string.Empty);
+ ExponentLayout = new OptionLayout(false, Exponent);
+ MidpointLayout = new OptionLayout(false, string.Empty);
+ LutTextLayout = new OptionLayout(true, LUTLayoutText);
+ LutPanelLayout = new OptionLayout(false, string.Empty);
+ }
+ }
+} \ No newline at end of file
diff --git a/grapher/Models/AccelGUI.cs b/grapher/Models/AccelGUI.cs
index 3a30a9b..9ca7fed 100644
--- a/grapher/Models/AccelGUI.cs
+++ b/grapher/Models/AccelGUI.cs
@@ -32,7 +32,7 @@ namespace grapher
AccelCharts = accelCharts;
ApplyOptions = applyOptions;
WriteButton = writeButton;
- ToggleButton = (CheckBox)toggleButton;
+ DisableButton = (CheckBox)toggleButton;
ScaleMenuItem = scaleMenuItem;
Settings = settings;
DefaultButtonFont = WriteButton.Font;
@@ -42,34 +42,17 @@ namespace grapher
ScaleMenuItem.Click += new System.EventHandler(OnScaleMenuItemClick);
WriteButton.Click += new System.EventHandler(OnWriteButtonClick);
- ToggleButton.Click += new System.EventHandler(OnToggleButtonClick);
+ DisableButton.Click += new System.EventHandler(DisableDriverEventHandler);
AccelForm.FormClosing += new FormClosingEventHandler(SaveGUISettingsOnClose);
- ButtonTimerInterval = Convert.ToInt32(DriverInterop.WriteDelayMs);
+ ButtonTimerInterval = Convert.ToInt32(DriverSettings.WriteDelayMs);
ButtonTimer = new Timer();
ButtonTimer.Tick += new System.EventHandler(OnButtonTimerTick);
ChartRefresh = SetupChartTimer();
- bool settingsActive = Settings.Startup();
- SettingsNotDefault = !Settings.RawAccelSettings.IsDefaultEquivalent();
-
- if (settingsActive)
- {
- LastToggleChecked = SettingsNotDefault;
- ToggleButton.Enabled = LastToggleChecked;
- RefreshOnRead(Settings.RawAccelSettings.AccelerationSettings);
- }
- else
- {
- DriverSettings active = DriverInterop.GetActiveSettings();
- bool activeNotDefault = !RawAccelSettings.IsDefaultEquivalent(active);
-
- LastToggleChecked = activeNotDefault;
- ToggleButton.Enabled = SettingsNotDefault || activeNotDefault;
- RefreshOnRead(active);
- }
-
+ RefreshUser();
+ RefreshActive();
SetupButtons();
// TODO: The below removes an overlapping form from the anisotropy panel.
@@ -94,7 +77,7 @@ namespace grapher
public Button WriteButton { get; }
- public CheckBox ToggleButton { get; }
+ public CheckBox DisableButton { get; }
public Timer ButtonTimer { get; }
@@ -104,8 +87,6 @@ namespace grapher
public DeviceIDManager DeviceIDManager { get; }
- public Action UpdateInputManagers { get; private set; }
-
private Timer ChartRefresh { get; }
private Font SmallButtonFont { get; }
@@ -125,47 +106,36 @@ namespace grapher
private void SaveGUISettingsOnClose(Object sender, FormClosingEventArgs e)
{
var guiSettings = Settings.MakeGUISettingsFromFields();
- if (!Settings.RawAccelSettings.GUISettings.Equals(guiSettings))
+ if (!Settings.GuiSettings.Equals(guiSettings))
{
- Settings.RawAccelSettings.GUISettings = guiSettings;
- Settings.RawAccelSettings.Save();
+ guiSettings.Save();
}
}
public void UpdateActiveSettingsFromFields()
{
- var driverSettings = Settings.RawAccelSettings.AccelerationSettings;
+ var settings = new DriverSettings();
- var newArgs = ApplyOptions.GetArgs();
- newArgs.x.speedCap = driverSettings.args.x.speedCap;
- newArgs.y.speedCap = driverSettings.args.y.speedCap;
-
- var settings = new DriverSettings
+ settings.rotation = ApplyOptions.Rotation.Field.Data;
+ settings.sensitivity = new Vec2<double>
{
- rotation = ApplyOptions.Rotation.Field.Data,
- snap = driverSettings.snap,
- sensitivity = new Vec2<double>
- {
- x = ApplyOptions.Sensitivity.Fields.X,
- y = ApplyOptions.Sensitivity.Fields.Y
- },
- combineMagnitudes = ApplyOptions.IsWhole,
- modes = ApplyOptions.GetModes(),
- args = newArgs,
- minimumTime = driverSettings.minimumTime,
- directionalMultipliers = driverSettings.directionalMultipliers,
- domainArgs = ApplyOptions.Directionality.GetDomainArgs(),
- rangeXY = ApplyOptions.Directionality.GetRangeXY(),
- deviceID = DeviceIDManager.ID,
+ x = ApplyOptions.Sensitivity.Fields.X,
+ y = ApplyOptions.Sensitivity.Fields.Y
};
+ settings.combineMagnitudes = ApplyOptions.IsWhole;
+ ApplyOptions.SetArgs(ref settings.args);
+ settings.domainArgs = ApplyOptions.Directionality.GetDomainArgs();
+ settings.rangeXY = ApplyOptions.Directionality.GetRangeXY();
+ settings.deviceID = DeviceIDManager.ID;
+
+ Settings.SetHiddenOptions(settings);
ButtonDelay(WriteButton);
- SettingsErrors errors = Settings.TryUpdateActiveSettings(settings);
+
+ SettingsErrors errors = Settings.TryActivate(settings);
if (errors.Empty())
{
- SettingsNotDefault = !Settings.RawAccelSettings.IsDefaultEquivalent();
- LastToggleChecked = SettingsNotDefault;
- RefreshOnRead(Settings.RawAccelSettings.AccelerationSettings);
+ RefreshActive();
}
else
{
@@ -173,25 +143,29 @@ namespace grapher
}
}
- public void RefreshOnRead(DriverSettings args)
+ public void UpdateInputManagers()
{
- UpdateShownActiveValues(args);
- UpdateGraph(args);
-
- UpdateInputManagers = () =>
- {
- MouseWatcher.UpdateHandles(args.deviceID);
- DeviceIDManager.Update(args.deviceID);
- };
+ MouseWatcher.UpdateHandles(Settings.ActiveSettings.baseSettings.deviceID);
+ DeviceIDManager.Update(Settings.ActiveSettings.baseSettings.deviceID);
+ }
+ public void RefreshActive()
+ {
+ UpdateShownActiveValues(Settings.UserSettings);
+ UpdateGraph();
UpdateInputManagers();
}
- public void UpdateGraph(DriverSettings args)
+ public void RefreshUser()
+ {
+ UpdateShownActiveValues(Settings.UserSettings);
+ }
+
+ public void UpdateGraph()
{
AccelCharts.Calculate(
Settings.ActiveAccel,
- args);
+ Settings.ActiveSettings.baseSettings);
AccelCharts.Bind();
}
@@ -215,32 +189,31 @@ namespace grapher
{
WriteButton.Top = Constants.SensitivityChartAloneHeight - Constants.ButtonVerticalOffset;
- ToggleButton.Appearance = Appearance.Button;
- ToggleButton.FlatStyle = FlatStyle.System;
- ToggleButton.TextAlign = ContentAlignment.MiddleCenter;
- ToggleButton.Size = WriteButton.Size;
- ToggleButton.Top = WriteButton.Top;
+ DisableButton.Appearance = Appearance.Button;
+ DisableButton.FlatStyle = FlatStyle.System;
+ DisableButton.TextAlign = ContentAlignment.MiddleCenter;
+ DisableButton.Size = WriteButton.Size;
+ DisableButton.Top = WriteButton.Top;
SetButtonDefaults();
}
private void SetButtonDefaults()
{
- ToggleButton.Checked = LastToggleChecked;
-
- ToggleButton.Font = DefaultButtonFont;
- ToggleButton.Text = ToggleButton.Checked ? "Disable" : "Enable";
- ToggleButton.Update();
+ DisableButton.Font = DefaultButtonFont;
+ DisableButton.Text = "Disable";
+ DisableButton.Enabled = true;
+ DisableButton.Update();
WriteButton.Font = DefaultButtonFont;
WriteButton.Text = Constants.WriteButtonDefaultText;
- WriteButton.Enabled = ToggleButton.Checked || !ToggleButton.Enabled;
+ WriteButton.Enabled = true;
WriteButton.Update();
}
private void OnScaleMenuItemClick(object sender, EventArgs e)
{
- UpdateGraph(Settings.RawAccelSettings.AccelerationSettings);
+ UpdateGraph();
}
private void OnWriteButtonClick(object sender, EventArgs e)
@@ -248,24 +221,16 @@ namespace grapher
UpdateActiveSettingsFromFields();
}
- private void OnToggleButtonClick(object sender, EventArgs e)
+ private void DisableDriverEventHandler(object sender, EventArgs e)
{
- var settings = ToggleButton.Checked ?
- Settings.RawAccelSettings.AccelerationSettings :
- DriverInterop.DefaultSettings;
-
- LastToggleChecked = ToggleButton.Checked;
- ButtonDelay(ToggleButton);
-
- SettingsManager.SendToDriver(settings);
- Settings.ActiveAccel.UpdateFromSettings(settings);
- RefreshOnRead(settings);
+ ButtonDelay(DisableButton);
+ Settings.DisableDriver();
+ RefreshActive();
}
private void OnButtonTimerTick(object sender, EventArgs e)
{
ButtonTimer.Stop();
- ToggleButton.Enabled = SettingsNotDefault;
SetButtonDefaults();
}
@@ -277,15 +242,13 @@ namespace grapher
private void ButtonDelay(ButtonBase btn)
{
- ToggleButton.Checked = false;
-
- ToggleButton.Enabled = false;
+ DisableButton.Enabled = false;
WriteButton.Enabled = false;
btn.Font = SmallButtonFont;
btn.Text = $"{Constants.ButtonDelayText} : {ButtonTimerInterval} ms";
- ToggleButton.Update();
+ DisableButton.Update();
WriteButton.Update();
StartButtonTimer();
diff --git a/grapher/Models/AccelGUIFactory.cs b/grapher/Models/AccelGUIFactory.cs
index 305445a..7d33f0c 100644
--- a/grapher/Models/AccelGUIFactory.cs
+++ b/grapher/Models/AccelGUIFactory.cs
@@ -3,6 +3,7 @@ using grapher.Models.Devices;
using grapher.Models.Mouse;
using grapher.Models.Options;
using grapher.Models.Options.Directionality;
+using grapher.Models.Options.LUT;
using grapher.Models.Serialized;
using System;
using System.Windows.Forms;
@@ -25,15 +26,13 @@ namespace grapher.Models
Chart gainChartY,
ComboBox accelTypeDropX,
ComboBox accelTypeDropY,
+ ComboBox lutApplyDropdownX,
+ ComboBox lutApplyDropdownY,
Button writeButton,
ButtonBase toggleButton,
ToolStripMenuItem showVelocityGainToolStripMenuItem,
ToolStripMenuItem showLastMouseMoveMenuItem,
ToolStripMenuItem streamingModeToolStripMenuItem,
- ToolStripMenuItem velocityGainCapToolStripMenuItem,
- ToolStripMenuItem legacyCapToolStripMenuItem,
- ToolStripMenuItem gainOffsetToolStripMenuItem,
- ToolStripMenuItem legacyOffsetToolStripMenuItem,
ToolStripMenuItem autoWriteMenuItem,
ToolStripMenuItem useSpecificDeviceMenuItem,
ToolStripMenuItem scaleMenuItem,
@@ -69,6 +68,12 @@ namespace grapher.Models
CheckBox fakeBox,
CheckBox wholeCheckBox,
CheckBox byComponentCheckBox,
+ CheckBox gainSwitchX,
+ CheckBox gainSwitchY,
+ RichTextBox xLutActiveValuesBox,
+ RichTextBox yLutActiveValuesBox,
+ RichTextBox xLutPointsBox,
+ RichTextBox yLutPointsBox,
Label lockXYLabel,
Label sensitivityLabel,
Label rotationLabel,
@@ -86,6 +91,8 @@ namespace grapher.Models
Label limitLabelY,
Label expLabelX,
Label expLabelY,
+ Label lutTextLabelX,
+ Label lutTextLabelY,
Label constantThreeLabelX,
Label constantThreeLabelY,
Label activeValueTitleX,
@@ -111,6 +118,8 @@ namespace grapher.Models
Label midpointActiveLabelY,
Label accelTypeActiveLabelX,
Label accelTypeActiveLabelY,
+ Label gainSwitchActiveLabelX,
+ Label gainSwitchActiveLabelY,
Label optionSetXTitle,
Label optionSetYTitle,
Label mouseLabel,
@@ -125,7 +134,11 @@ namespace grapher.Models
Label domainActiveValueY,
Label rangeLabel,
Label rangeActiveValueX,
- Label rangeActiveValueY)
+ Label rangeActiveValueY,
+ Label lutApplyLabelX,
+ Label lutApplyLabelY,
+ Label lutApplyActiveValueX,
+ Label lutApplyActiveValueY)
{
fakeBox.Checked = false;
fakeBox.Hide();
@@ -224,16 +237,6 @@ namespace grapher.Models
new ActiveValueLabel(offsetActiveLabelY, activeValueTitleY),
"Offset");
- var offsetOptionsX = new OffsetOptions(
- gainOffsetToolStripMenuItem,
- legacyOffsetToolStripMenuItem,
- offsetX);
-
- var offsetOptionsY = new OffsetOptions(
- gainOffsetToolStripMenuItem,
- legacyOffsetToolStripMenuItem,
- offsetY);
-
var accelerationX = new Option(
new Field(accelerationBoxX, form, 0),
constantOneLabelX,
@@ -324,39 +327,53 @@ namespace grapher.Models
new ActiveValueLabel(rangeActiveValueY, direcionalityActiveValueTitle)),
false);
- var capOptionsX = new CapOptions(
- velocityGainCapToolStripMenuItem,
- legacyCapToolStripMenuItem,
- capX);
- var capOptionsY = new CapOptions(
- velocityGainCapToolStripMenuItem,
- legacyCapToolStripMenuItem,
- capY);
+ var lutTextX = new TextOption(lutTextLabelX);
+ var lutTextY = new TextOption(lutTextLabelY);
+ var gainSwitchOptionX = new CheckBoxOption(
+ gainSwitchX,
+ new ActiveValueLabel(gainSwitchActiveLabelX, activeValueTitleX));
+ var gainSwitchOptionY = new CheckBoxOption(
+ gainSwitchY,
+ new ActiveValueLabel(gainSwitchActiveLabelY, activeValueTitleY));
var accelerationOptionsX = new AccelTypeOptions(
accelTypeDropX,
+ gainSwitchOptionX,
accelerationX,
scaleX,
- capOptionsX,
+ capX,
weightX,
- offsetOptionsX,
+ offsetX,
limitX,
exponentX,
midpointX,
+ lutTextX,
+ new LUTPanelOptions(xLutPointsBox, xLutActiveValuesBox),
+ new LutApplyOptions(
+ lutApplyLabelX,
+ lutApplyDropdownX,
+ new ActiveValueLabel(lutApplyActiveValueX, activeValueTitleX)),
writeButton,
new ActiveValueLabel(accelTypeActiveLabelX, activeValueTitleX));
var accelerationOptionsY = new AccelTypeOptions(
accelTypeDropY,
+ gainSwitchOptionY,
accelerationY,
scaleY,
- capOptionsY,
+ capY,
weightY,
- offsetOptionsY,
+ offsetY,
limitY,
exponentY,
midpointY,
+ lutTextY,
+ new LUTPanelOptions(yLutPointsBox, yLutActiveValuesBox),
+ new LutApplyOptions(
+ lutApplyLabelY,
+ lutApplyDropdownY,
+ new ActiveValueLabel(lutApplyActiveValueY, activeValueTitleY)),
writeButton,
new ActiveValueLabel(accelTypeActiveLabelY, activeValueTitleY));
@@ -383,7 +400,7 @@ namespace grapher.Models
range,
wholeCheckBox,
byComponentCheckBox,
- 260);
+ Constants.DirectionalityVerticalOffset);
var applyOptions = new ApplyOptions(
byComponentXYLock,
diff --git a/grapher/Models/Calculations/AccelCalculator.cs b/grapher/Models/Calculations/AccelCalculator.cs
index d641873..574f55a 100644
--- a/grapher/Models/Calculations/AccelCalculator.cs
+++ b/grapher/Models/Calculations/AccelCalculator.cs
@@ -108,7 +108,7 @@ namespace grapher.Models.Calculations
}
var output = accel.Accelerate(simulatedInputDatum.x, simulatedInputDatum.y, simulatedInputDatum.time);
- var outMagnitude = Velocity(output.Item1, output.Item2, simulatedInputDatum.time);
+ var outMagnitude = DecimalCheck(Velocity(output.Item1, output.Item2, simulatedInputDatum.time));
var inDiff = Math.Round(simulatedInputDatum.velocity - lastInputMagnitude, 5);
var outDiff = Math.Round(outMagnitude - lastOutputMagnitude, 5);
@@ -133,8 +133,8 @@ namespace grapher.Models.Calculations
logIndex++;
}
- var ratio = outMagnitude / simulatedInputDatum.velocity;
- var slope = inDiff > 0 ? outDiff / inDiff : starter;
+ var ratio = DecimalCheck(outMagnitude / simulatedInputDatum.velocity);
+ var slope = DecimalCheck(inDiff > 0 ? outDiff / inDiff : starter);
if (slope < lastSlope)
{
@@ -200,9 +200,6 @@ namespace grapher.Models.Calculations
double maxSlope = 0.0;
double minSlope = Double.MaxValue;
-
- Sensitivity = GetSens(ref settings);
-
int angleIndex = 0;
foreach (var simulatedInputDataAngle in simulatedInputData)
@@ -223,7 +220,7 @@ namespace grapher.Models.Calculations
}
var output = accel.Accelerate(simulatedInputDatum.x, simulatedInputDatum.y, simulatedInputDatum.time);
- var magnitude = Velocity(output.Item1, output.Item2, simulatedInputDatum.time);
+ var magnitude = DecimalCheck(Velocity(output.Item1, output.Item2, simulatedInputDatum.time));
var inDiff = Math.Round(simulatedInputDatum.velocity - lastInputMagnitude, 5);
var outDiff = Math.Round(magnitude - lastOutputMagnitude, 5);
@@ -248,10 +245,15 @@ namespace grapher.Models.Calculations
logIndex++;
}
- var ratio = magnitude / simulatedInputDatum.velocity;
- var slope = inDiff > 0 ? outDiff / inDiff : settings.sensitivity.x;
+ var ratio = DecimalCheck(magnitude / simulatedInputDatum.velocity);
+ var slope = DecimalCheck(inDiff > 0 ? outDiff / inDiff : settings.sensitivity.x);
bool indexToMeasureExtrema = (angleIndex == 0) || (angleIndex == (Constants.AngleDivisions - 1));
+
+ if (angleIndex == 0 && double.IsNaN(ratio))
+ {
+ Console.WriteLine("oops");
+ }
if (indexToMeasureExtrema && (ratio > maxRatio))
{
@@ -322,10 +324,9 @@ namespace grapher.Models.Calculations
mouseInputData.x = ceilX;
mouseInputData.y = ceilY;
mouseInputData.time = MeasurementTime*timeFactor;
- mouseInputData.velocity = Velocity(ceilX, ceilY, mouseInputData.time);
+ mouseInputData.velocity = DecimalCheck(Velocity(ceilX, ceilY, mouseInputData.time));
mouseInputData.angle = Math.Atan2(ceilY, ceilX);
magnitudes.Add(mouseInputData);
-
}
for (double i = 5; i < MaxVelocity; i+=Increment)
@@ -336,7 +337,7 @@ namespace grapher.Models.Calculations
mouseInputData.x = ceil;
mouseInputData.y = 0;
mouseInputData.time = MeasurementTime * timeFactor;
- mouseInputData.velocity = Velocity(ceil, 0, mouseInputData.time);
+ mouseInputData.velocity = DecimalCheck(Velocity(ceil, 0, mouseInputData.time));
mouseInputData.angle = Math.Atan2(ceil, 0);
magnitudes.Add(mouseInputData);
}
@@ -371,7 +372,7 @@ namespace grapher.Models.Calculations
mouseInputData.x = ceil;
mouseInputData.y = 0;
mouseInputData.time = MeasurementTime*timeFactor;
- mouseInputData.velocity = Velocity(ceil, 0, mouseInputData.time);
+ mouseInputData.velocity = DecimalCheck(Velocity(ceil, 0, mouseInputData.time));
mouseInputData.angle = 0;
magnitudes.Add(mouseInputData);
}
@@ -391,7 +392,7 @@ namespace grapher.Models.Calculations
mouseInputData.x = 0;
mouseInputData.y = ceil;
mouseInputData.time = MeasurementTime*timeFactor;
- mouseInputData.velocity = Velocity(0, ceil, mouseInputData.time);
+ mouseInputData.velocity = DecimalCheck(Velocity(0, ceil, mouseInputData.time));
mouseInputData.angle = 0;
magnitudes.Add(mouseInputData);
}
@@ -404,7 +405,7 @@ namespace grapher.Models.Calculations
mouseInputData.x = 0;
mouseInputData.y = ceil;
mouseInputData.time = MeasurementTime*timeFactor;
- mouseInputData.velocity = Velocity(0, ceil, mouseInputData.time);
+ mouseInputData.velocity = DecimalCheck(Velocity(0, ceil, mouseInputData.time));
mouseInputData.angle = Math.PI / 2;
magnitudes.Add(mouseInputData);
}
@@ -422,39 +423,12 @@ namespace grapher.Models.Calculations
foreach (var slowMoveMagnitude in SlowMovements)
{
- var slowMoveX = Math.Round(slowMoveMagnitude * Math.Cos(angle), 4);
- var slowMoveY = Math.Round(slowMoveMagnitude * Math.Sin(angle), 4);
- var ceilX = (int)Math.Round(slowMoveX*90);
- var ceilY = (int)Math.Round(slowMoveY*90);
- var ceilMagnitude = Magnitude(ceilX, ceilY);
- var timeFactor = ceilMagnitude / slowMoveMagnitude;
-
- SimulatedMouseInput mouseInputData;
- mouseInputData.x = ceilX;
- mouseInputData.y = ceilY;
- mouseInputData.time = timeFactor;
- mouseInputData.velocity = Velocity(ceilX, ceilY, timeFactor);
- mouseInputData.angle = angle;
- magnitudes.Add(mouseInputData);
+ magnitudes.Add(SimulateAngledInput(angle, slowMoveMagnitude));
}
for (double magnitude = 5; magnitude < MaxVelocity; magnitude+=Increment)
{
- var slowMoveX = Math.Round(magnitude * Math.Cos(angle), 4);
- var slowMoveY = Math.Round(magnitude * Math.Sin(angle), 4);
- var ratio = slowMoveX > 0.0 ? slowMoveY / slowMoveX : 90;
- var ceilX = (int)Math.Round(slowMoveX*90);
- var ceilY = (int)Math.Round(slowMoveY*ratio);
- var ceilMagnitude = Magnitude(ceilX, ceilY);
- var timeFactor = ceilMagnitude / magnitude;
-
- SimulatedMouseInput mouseInputData;
- mouseInputData.x = ceilX;
- mouseInputData.y = ceilY;
- mouseInputData.time = timeFactor;
- mouseInputData.velocity = Velocity(ceilX, ceilY, mouseInputData.time);
- mouseInputData.angle = angle;
- magnitudes.Add(mouseInputData);
+ magnitudes.Add(SimulateAngledInput(angle, magnitude));
}
magnitudesByAngle.Add(magnitudes.AsReadOnly());
@@ -465,11 +439,31 @@ namespace grapher.Models.Calculations
public static double Magnitude(int x, int y)
{
+ if (x == 0)
+ {
+ return Math.Abs(y);
+ }
+
+ if (y == 0)
+ {
+ return Math.Abs(x);
+ }
+
return Math.Sqrt(x * x + y * y);
}
public static double Magnitude(double x, double y)
{
+ if (x == 0)
+ {
+ return Math.Abs(y);
+ }
+
+ if (y == 0)
+ {
+ return Math.Abs(x);
+ }
+
return Math.Sqrt(x * x + y * y);
}
@@ -519,6 +513,94 @@ namespace grapher.Models.Calculations
SimulatedDirectionalInput = GetSimulatedDirectionalInput();
}
+ private static readonly double MinChartAllowedValue = Convert.ToDouble(Decimal.MinValue) / 10;
+ private static readonly double MaxChartAllowedValue = Convert.ToDouble(Decimal.MaxValue) / 10;
+
+ private double DecimalCheck(double value)
+ {
+ if (value < MinChartAllowedValue)
+ {
+ return MinChartAllowedValue;
+ }
+
+ if (value > MaxChartAllowedValue)
+ {
+ return MaxChartAllowedValue;
+ }
+
+ return value;
+ }
+
+ private SimulatedMouseInput SimulateAngledInput(double angle, double magnitude)
+ {
+ SimulatedMouseInput mouseInputData;
+
+ var moveX = Math.Round(magnitude * Math.Cos(angle), 4);
+ var moveY = Math.Round(magnitude * Math.Sin(angle), 4);
+
+ if (moveX == 0)
+ {
+ mouseInputData.x = 0;
+ mouseInputData.y = (int)Math.Ceiling(moveY);
+ mouseInputData.time = mouseInputData.y / moveY;
+ }
+ else if (moveY == 0)
+ {
+ mouseInputData.x = (int)Math.Ceiling(moveX);
+ mouseInputData.y = 0;
+ mouseInputData.time = mouseInputData.x / moveX;
+ }
+ else
+ {
+ var ratio = moveY / moveX;
+ int ceilX = 0;
+ int ceilY = 0;
+ double biggerX = 0;
+ double biggerY = 0;
+ double roundedBiggerX = 0;
+ double roundedBiggerY = 0;
+ double roundedRatio = -1;
+ double factor = 10;
+
+ while (Math.Abs(roundedRatio - ratio) > 0.01 &&
+ biggerX < 25000 &&
+ biggerY < 25000)
+ {
+ roundedBiggerX = Math.Floor(biggerX);
+ roundedBiggerY = Math.Floor(biggerY);
+ ceilX = Convert.ToInt32(roundedBiggerX);
+ ceilY = Convert.ToInt32(roundedBiggerY);
+ roundedRatio = ceilX > 0 ? ceilY / ceilX : -1;
+ biggerX = moveX * factor;
+ biggerY = moveY * factor;
+ factor *= 10;
+ }
+
+ var ceilMagnitude = Magnitude(ceilX, ceilY);
+ var timeFactor = ceilMagnitude / magnitude;
+
+ mouseInputData.x = ceilX;
+ mouseInputData.y = ceilY;
+ mouseInputData.time = timeFactor;
+
+ if (mouseInputData.x == 1 && mouseInputData.time == 1)
+ {
+ Console.WriteLine("Oops");
+ }
+
+ }
+
+ mouseInputData.velocity = DecimalCheck(Velocity(mouseInputData.x, mouseInputData.y, mouseInputData.time));
+
+ if (double.IsNaN(mouseInputData.velocity))
+ {
+ Console.WriteLine("oopsie");
+ }
+
+ mouseInputData.angle = angle;
+ return mouseInputData;
+ }
+
#endregion Methods
}
}
diff --git a/grapher/Models/Mouse/MouseWatcher.cs b/grapher/Models/Mouse/MouseWatcher.cs
index 163829f..c5c2ae5 100644
--- a/grapher/Models/Mouse/MouseWatcher.cs
+++ b/grapher/Models/Mouse/MouseWatcher.cs
@@ -772,8 +772,8 @@ namespace grapher.Models.Mouse
// strip negative directional multipliers, charts calculated from positive input
- Vec2<double> dirMults = SettingsManager.RawAccelSettings
- .AccelerationSettings.directionalMultipliers;
+ Vec2<double> dirMults = SettingsManager.ActiveSettings.baseSettings
+ .directionalMultipliers;
if (dirMults.x > 0 && x < 0)
{
diff --git a/grapher/Models/Options/AccelOptionSet.cs b/grapher/Models/Options/AccelOptionSet.cs
index 11a7f10..75eb017 100644
--- a/grapher/Models/Options/AccelOptionSet.cs
+++ b/grapher/Models/Options/AccelOptionSet.cs
@@ -46,8 +46,8 @@ namespace grapher.Models.Options
{
IsTitleMode = false;
- HideTitle();
Options.ShowFull();
+ HideTitle();
}
}
@@ -61,6 +61,7 @@ namespace grapher.Models.Options
Options.ShowShortened();
DisplayTitle();
+ Options.HandleLUTOptionsOnResize();
}
}
@@ -103,16 +104,11 @@ namespace grapher.Models.Options
Options.SetArgs(ref args);
}
- public AccelArgs GenerateArgs()
- {
- return Options.GenerateArgs();
- }
-
- public void SetActiveValues(int mode, AccelArgs args)
+ public void SetActiveValues(ref AccelArgs args)
{
if (!Hidden)
{
- Options.SetActiveValues(mode, args);
+ Options.SetActiveValues(ref args);
}
}
diff --git a/grapher/Models/Options/AccelTypeOptions.cs b/grapher/Models/Options/AccelTypeOptions.cs
index 8d3fecb..f97df2d 100644
--- a/grapher/Models/Options/AccelTypeOptions.cs
+++ b/grapher/Models/Options/AccelTypeOptions.cs
@@ -1,5 +1,6 @@
using grapher.Layouts;
using grapher.Models.Options;
+using grapher.Models.Options.LUT;
using grapher.Models.Serialized;
using System;
using System.Collections.Generic;
@@ -12,16 +13,15 @@ namespace grapher
{
#region Fields
- public static readonly Dictionary<string, LayoutBase> AccelerationTypes = new List<LayoutBase>
- {
- new LinearLayout(),
- new ClassicLayout(),
- new NaturalLayout(),
- new NaturalGainLayout(),
- new PowerLayout(),
- new MotivityLayout(),
- new OffLayout()
- }.ToDictionary(k => k.Name);
+ public static readonly LayoutBase Linear = new LinearLayout();
+ public static readonly LayoutBase Classic = new ClassicLayout();
+ public static readonly LayoutBase Jump = new JumpLayout();
+ public static readonly LayoutBase Natural = new NaturalLayout();
+ public static readonly LayoutBase Motivity = new MotivityLayout();
+ public static readonly LayoutBase Power = new PowerLayout();
+ public static readonly LayoutBase LUT = new LUTLayout();
+ public static readonly LayoutBase Off = new OffLayout();
+ public static readonly LayoutBase Unsupported = new UnsupportedLayout();
#endregion Fields
@@ -29,22 +29,39 @@ namespace grapher
public AccelTypeOptions(
ComboBox accelDropdown,
+ CheckBoxOption gainSwitch,
Option acceleration,
Option scale,
- CapOptions cap,
+ Option cap,
Option weight,
- OffsetOptions offset,
+ Option offset,
Option limit,
Option exponent,
Option midpoint,
+ TextOption lutText,
+ LUTPanelOptions lutPanelOptions,
+ LutApplyOptions lutApplyOptions,
Button writeButton,
ActiveValueLabel accelTypeActiveValue)
{
AccelDropdown = accelDropdown;
AccelDropdown.Items.Clear();
- AccelDropdown.Items.AddRange(AccelerationTypes.Keys.ToArray());
+ AccelDropdown.Items.AddRange(
+ new LayoutBase[]
+ {
+ Linear,
+ Classic,
+ Jump,
+ Natural,
+ Motivity,
+ Power,
+ LUT,
+ Off
+ });
+
AccelDropdown.SelectedIndexChanged += new System.EventHandler(OnIndexChanged);
+ GainSwitch = gainSwitch;
Acceleration = acceleration;
Scale = scale;
Cap = cap;
@@ -55,11 +72,21 @@ namespace grapher
Midpoint = midpoint;
WriteButton = writeButton;
AccelTypeActiveValue = accelTypeActiveValue;
+ LutText = lutText;
+ LutPanel = lutPanelOptions;
+ LutApply = lutApplyOptions;
AccelTypeActiveValue.Left = AccelDropdown.Left + AccelDropdown.Width;
AccelTypeActiveValue.Height = AccelDropdown.Height;
+ GainSwitch.Left = Acceleration.Field.Left;
+
+ LutPanel.Left = AccelDropdown.Left;
+ LutPanel.Width = AccelDropdown.Width + AccelTypeActiveValue.Width;
- Layout("Off");
+ LutText.SetText(TextOption.LUTLayoutExpandedText, TextOption.LUTLayoutShortenedText);
+
+ AccelerationType = Off;
+ Layout();
ShowingDefault = true;
}
@@ -72,27 +99,17 @@ namespace grapher
public ComboBox AccelDropdown { get; }
- public int AccelerationIndex
- {
- get
- {
- return AccelerationType.Index;
- }
- }
-
- public LayoutBase AccelerationType { get; private set; }
-
public ActiveValueLabel AccelTypeActiveValue { get; }
public Option Acceleration { get; }
public Option Scale { get; }
- public CapOptions Cap { get; }
+ public Option Cap { get; }
public Option Weight { get; }
- public OffsetOptions Offset { get; }
+ public Option Offset { get; }
public Option Limit { get; }
@@ -100,6 +117,26 @@ namespace grapher
public Option Midpoint { get; }
+ public TextOption LutText { get; }
+
+ public CheckBoxOption GainSwitch { get; }
+
+ public LUTPanelOptions LutPanel { get; }
+
+ public LutApplyOptions LutApply { get; }
+
+ public LayoutBase AccelerationType
+ {
+ get
+ {
+ return AccelDropdown.SelectedItem as LayoutBase;
+ }
+ private set
+ {
+ AccelDropdown.SelectedItem = value;
+ }
+ }
+
public override int Top
{
get
@@ -131,6 +168,9 @@ namespace grapher
set
{
AccelDropdown.Left = value;
+ LutText.Left = value;
+ LutPanel.Left = value;
+ LutApply.Left = value;
}
}
@@ -143,6 +183,9 @@ namespace grapher
set
{
AccelDropdown.Width = value;
+ LutText.Width = value;
+ LutPanel.Width = AccelTypeActiveValue.CenteringLabel.Right - AccelDropdown.Left;
+ LutApply.Width = value;
}
}
@@ -165,6 +208,7 @@ namespace grapher
AccelDropdown.Hide();
AccelTypeActiveValue.Hide();
+ GainSwitch.Hide();
Acceleration.Hide();
Scale.Hide();
Cap.Hide();
@@ -173,13 +217,16 @@ namespace grapher
Limit.Hide();
Exponent.Hide();
Midpoint.Hide();
+ LutText.Hide();
+ LutPanel.Hide();
+ LutApply.Hide();
}
public void Show()
{
AccelDropdown.Show();
AccelTypeActiveValue.Show();
- Layout();
+ Layout(AccelDropdown.Bottom + Constants.OptionVerticalSeperation);
}
public override void Show(string name)
@@ -187,20 +234,21 @@ namespace grapher
Show();
}
- public void SetActiveValues(int index, AccelArgs args)
+ public void SetActiveValues(ref AccelArgs args)
{
- AccelerationType = AccelerationTypes.Where(t => t.Value.Index == index).FirstOrDefault().Value;
- AccelTypeActiveValue.SetValue(AccelerationType.Name);
- AccelDropdown.SelectedIndex = AccelerationType.Index;
-
+ AccelerationType = AccelTypeFromSettings(ref args);
+ AccelTypeActiveValue.SetValue(AccelerationType.ActiveName);
+ GainSwitch.SetActiveValue(args.legacy);
Weight.SetActiveValue(args.weight);
- Cap.SetActiveValues(args.gainCap, args.scaleCap, args.gainCap > 0 || args.scaleCap <= 0);
- Offset.SetActiveValue(args.offset, args.legacyOffset);
- Acceleration.SetActiveValue(args.acceleration);
+ Cap.SetActiveValue(args.cap);
+ Offset.SetActiveValue(args.offset);
+ Acceleration.SetActiveValue(AccelerationParameterFromArgs(ref args));
Scale.SetActiveValue(args.scale);
Limit.SetActiveValue(args.limit);
- Exponent.SetActiveValue(args.exponent);
+ Exponent.SetActiveValue(ExponentParameterFromArgs(ref args));
Midpoint.SetActiveValue(args.midpoint);
+ LutPanel.SetActiveValues(args.tableData.points, args.tableData.length);
+ LutApply.SetActiveValue(args.tableData.velocity);
}
public void ShowFull()
@@ -212,6 +260,9 @@ namespace grapher
Left = Acceleration.Left + Constants.DropDownLeftSeparation;
Width = Acceleration.Width - Constants.DropDownLeftSeparation;
+
+ LutText.Expand();
+ HandleLUTOptionsOnResize();
}
public void ShowShortened()
@@ -223,33 +274,66 @@ namespace grapher
Left = Acceleration.Field.Left;
Width = Acceleration.Field.Width;
+
+ LutText.Shorten();
}
public void SetArgs(ref AccelArgs args)
{
- AccelArgs defaults = DriverInterop.DefaultSettings.args.x;
- args.acceleration = Acceleration.Visible ? Acceleration.Field.Data : defaults.acceleration;
- args.scale = Scale.Visible ? Scale.Field.Data : defaults.scale;
- args.gainCap = Cap.Visible ? Cap.VelocityGainCap : defaults.gainCap;
- args.scaleCap = Cap.Visible ? Cap.SensitivityCap : defaults.scaleCap;
- args.limit = Limit.Visible ? Limit.Field.Data : defaults.limit;
- args.exponent = Exponent.Visible ? Exponent.Field.Data : defaults.exponent;
- args.offset = Offset.Visible ? Offset.Offset : defaults.offset;
- args.legacyOffset = Offset.IsLegacy;
- args.midpoint = Midpoint.Visible ? Midpoint.Field.Data : defaults.midpoint;
- args.weight = Weight.Visible ? Weight.Field.Data : defaults.weight;
- }
+ if (AccelerationType == Unsupported) throw new NotImplementedException();
- public AccelArgs GenerateArgs()
- {
- AccelArgs args = new AccelArgs();
- SetArgs(ref args);
- return args;
+ args.mode = AccelerationType.Mode;
+
+ if (Acceleration.Visible)
+ {
+ if (args.mode == AccelMode.natural)
+ {
+ args.decayRate = Acceleration.Field.Data;
+ }
+ else if (args.mode == AccelMode.motivity)
+ {
+ args.growthRate = Acceleration.Field.Data;
+ }
+ else
+ {
+ args.accelClassic = Acceleration.Field.Data;
+ }
+
+ args.smooth = Acceleration.Field.Data;
+ }
+
+ args.legacy = !GainSwitch.CheckBox.Checked;
+
+ if (Scale.Visible) args.scale = Scale.Field.Data;
+ if (Cap.Visible) args.cap = Cap.Field.Data;
+ if (Limit.Visible) args.limit = Limit.Field.Data;
+ if (Exponent.Visible)
+ {
+ if (args.mode == AccelMode.classic)
+ {
+ args.power = Exponent.Field.Data;
+ }
+ else
+ {
+ args.exponent = Exponent.Field.Data;
+ }
+ }
+ if (Offset.Visible) args.offset = Offset.Field.Data;
+ if (Midpoint.Visible) args.midpoint = Midpoint.Field.Data;
+ if (Weight.Visible) args.weight = Weight.Field.Data;
+ if (LutPanel.Visible)
+ {
+ (var points, var length) = LutPanel.GetPoints();
+ args.tableData.points = points;
+ args.tableData.length = length;
+ }
+ if (LutApply.Visible) args.tableData.velocity = LutApply.ApplyType == LutApplyOptions.LutApplyType.Velocity;
}
public override void AlignActiveValues()
{
AccelTypeActiveValue.Align();
+ GainSwitch.AlignActiveValues();
Acceleration.AlignActiveValues();
Scale.AlignActiveValues();
Cap.AlignActiveValues();
@@ -258,29 +342,33 @@ namespace grapher
Limit.AlignActiveValues();
Exponent.AlignActiveValues();
Midpoint.AlignActiveValues();
+ LutApply.AlignActiveValues();
}
- private void OnIndexChanged(object sender, EventArgs e)
+ public void HandleLUTOptionsOnResize()
{
- var accelerationTypeString = AccelDropdown.SelectedItem.ToString();
- Layout(accelerationTypeString, Beneath);
- ShowingDefault = false;
+ LutText.Left = AccelDropdown.Left;
+ LutPanel.Left = GainSwitch.Left - 100;
+ LutPanel.Width = Acceleration.ActiveValueLabel.CenteringLabel.Right - LutPanel.Left;
+ LutApply.Left = LutPanel.Left;
+ LutApply.Width = AccelDropdown.Right - LutPanel.Left;
}
- private void Layout(string type, int top = -1)
+ private void OnIndexChanged(object sender, EventArgs e)
{
- AccelerationType = AccelerationTypes[type];
- Layout(top);
+ Layout(Beneath);
+ ShowingDefault = false;
}
private void Layout(int top = -1)
{
if (top < 0)
{
- top = Acceleration.Top;
+ top = GainSwitch.Top;
}
AccelerationType.Layout(
+ GainSwitch,
Acceleration,
Scale,
Cap,
@@ -289,9 +377,64 @@ namespace grapher
Limit,
Exponent,
Midpoint,
+ LutText,
+ LutPanel,
+ LutApply,
top);
}
+ private LayoutBase AccelTypeFromSettings(ref AccelArgs args)
+ {
+ if (args.spacedTableArgs.mode != SpacedTableMode.off)
+ {
+ if (!AccelDropdown.Items.Contains(Unsupported))
+ {
+ AccelDropdown.Items.Add(Unsupported);
+ }
+
+ return Unsupported;
+ }
+
+ switch (args.mode)
+ {
+ case AccelMode.classic: return (args.power == 2) ? Linear : Classic;
+ case AccelMode.jump: return Jump;
+ case AccelMode.natural: return Natural;
+ case AccelMode.motivity: return Motivity;
+ case AccelMode.power: return Power;
+ case AccelMode.lut: return LUT;
+ default: return Off;
+ }
+ }
+
+ private double AccelerationParameterFromArgs(ref AccelArgs args)
+ {
+ if (args.mode == AccelMode.motivity)
+ {
+ return args.growthRate;
+ }
+ else if (args.mode == AccelMode.natural)
+ {
+ return args.decayRate;
+ }
+ else
+ {
+ return args.accelClassic;
+ }
+ }
+
+ private double ExponentParameterFromArgs(ref AccelArgs args)
+ {
+ if (args.mode == AccelMode.classic)
+ {
+ return args.power;
+ }
+ else
+ {
+ return args.exponent;
+ }
+ }
+
#endregion Methods
}
}
diff --git a/grapher/Models/Options/ApplyOptions.cs b/grapher/Models/Options/ApplyOptions.cs
index ffe430d..06854b8 100644
--- a/grapher/Models/Options/ApplyOptions.cs
+++ b/grapher/Models/Options/ApplyOptions.cs
@@ -81,59 +81,30 @@ namespace grapher.Models.Options
#region Methods
- public Vec2<AccelMode> GetModes()
+ public void SetArgs(ref Vec2<AccelArgs> args)
{
- var xMode = (AccelMode)OptionSetX.Options.AccelerationIndex;
+ OptionSetX.SetArgs(ref args.x);
- return new Vec2<AccelMode>
+ if (ByComponentVectorXYLock.Checked)
{
- x = xMode,
- y = ByComponentVectorXYLock.Checked ? xMode : (AccelMode)OptionSetY.Options.AccelerationIndex
- };
- }
-
- public Vec2<AccelArgs> GetArgs()
- {
- var xArgs = OptionSetX.GenerateArgs();
-
- return new Vec2<AccelArgs>
+ OptionSetX.SetArgs(ref args.y);
+ }
+ else
{
- x = xArgs,
- y = ByComponentVectorXYLock.Checked ? xArgs : OptionSetY.GenerateArgs()
- };
-
- }
-
- public void SetActiveValues(
- double xSens,
- double ySens,
- double rotation,
- int xMode,
- int yMode,
- AccelArgs xArgs,
- AccelArgs yArgs,
- bool isWhole)
- {
- Sensitivity.SetActiveValues(xSens, ySens);
- Rotation.SetActiveValue(rotation);
- OptionSetX.SetActiveValues(xMode, xArgs);
- WholeVectorCheckBox.Checked = isWhole;
- ByComponentVectorCheckBox.Checked = !isWhole;
- ByComponentVectorXYLock.Checked = xArgs.Equals(yArgs);
- OptionSetY.SetActiveValues(yMode, yArgs);
+ OptionSetY.SetArgs(ref args.y);
+ }
}
public void SetActiveValues(DriverSettings settings)
{
- SetActiveValues(
- settings.sensitivity.x,
- settings.sensitivity.y,
- settings.rotation,
- (int)settings.modes.x,
- (int)settings.modes.y,
- settings.args.x,
- settings.args.y,
- settings.combineMagnitudes);
+ Sensitivity.SetActiveValues(settings.sensitivity.x, settings.sensitivity.y);
+ Rotation.SetActiveValue(settings.rotation);
+
+ WholeVectorCheckBox.Checked = settings.combineMagnitudes;
+ ByComponentVectorCheckBox.Checked = !settings.combineMagnitudes;
+ ByComponentVectorXYLock.Checked = settings.args.x.Equals(settings.args.y);
+ OptionSetX.SetActiveValues(ref settings.args.x);
+ OptionSetY.SetActiveValues(ref settings.args.y);
Directionality.SetActiveValues(settings);
diff --git a/grapher/Models/Options/CapOptions.cs b/grapher/Models/Options/CapOptions.cs
deleted file mode 100644
index c459c50..0000000
--- a/grapher/Models/Options/CapOptions.cs
+++ /dev/null
@@ -1,225 +0,0 @@
-using grapher.Models.Options;
-using System;
-using System.Windows.Forms;
-
-namespace grapher
-{
- public class CapOptions : OptionBase
- {
- #region Constants
-
-
- #endregion Constants
-
- #region Constructors
-
- public CapOptions(
- ToolStripMenuItem velocityGainCapCheck,
- ToolStripMenuItem legacyCapCheck,
- Option capOption)
- {
-
- VelocityGainCapCheck = velocityGainCapCheck;
- LegacyCapCheck = legacyCapCheck;
- CapOption = capOption;
-
- LegacyCapCheck.Click += new System.EventHandler(OnSensitivityCapCheckClick);
- VelocityGainCapCheck.Click += new System.EventHandler(OnVelocityGainCapCheckClick);
-
- LegacyCapCheck.CheckedChanged += new System.EventHandler(OnSensitivityCapCheckedChange);
- VelocityGainCapCheck.CheckedChanged += new System.EventHandler(OnVelocityGainCapCheckedChange);
-
- EnableVelocityGainCap();
- }
-
- #endregion Constructors
-
- #region Properties
-
- public ToolStripMenuItem LegacyCapCheck { get; }
-
- public ToolStripMenuItem VelocityGainCapCheck { get; }
-
- public Option CapOption { get; }
-
- public bool IsSensitivityGain { get; private set; }
-
- public double SensitivityCap {
- get
- {
- if (IsSensitivityGain)
- {
- return CapOption.Field.Data;
- }
- else
- {
- return 0;
- }
- }
- }
-
- public double VelocityGainCap {
- get
- {
- if (IsSensitivityGain)
- {
- return 0;
- }
- else
- {
- return CapOption.Field.Data;
- }
- }
- }
-
- public override int Top
- {
- get
- {
- return CapOption.Top;
- }
- set
- {
- CapOption.Top = value;
- }
- }
-
- public override int Height
- {
- get
- {
- return CapOption.Height;
- }
- }
-
- public override int Left
- {
- get
- {
- return CapOption.Left;
- }
- set
- {
- CapOption.Left = value;
- }
- }
-
- public override int Width
- {
- get
- {
- return CapOption.Width;
- }
- set
- {
- CapOption.Width = value;
- }
- }
-
- public override bool Visible
- {
- get
- {
- return CapOption.Visible;
- }
- }
-
- #endregion Properties
-
- #region Methods
-
- public override void Hide()
- {
- CapOption.Hide();
- }
-
- public void Show()
- {
- CapOption.Show();
- }
-
- public override void Show(string name)
- {
- CapOption.Show(name);
- }
-
- public void SnapTo(Option option)
- {
- Top = option.Top + option.Height + Constants.OptionVerticalSeperation;
- }
-
-
- public void SetActiveValues(double gainCap, double sensCap, bool capGainEnabled)
- {
- if (capGainEnabled)
- {
- CapOption.ActiveValueLabel.FormatString = Constants.GainCapFormatString;
- CapOption.ActiveValueLabel.Prefix = "Gain";
- CapOption.SetActiveValue(gainCap);
- LegacyCapCheck.Checked = false;
- VelocityGainCapCheck.Checked = true;
- }
- else
- {
- CapOption.ActiveValueLabel.FormatString = Constants.DefaultActiveValueFormatString;
- CapOption.ActiveValueLabel.Prefix = string.Empty;
- CapOption.SetActiveValue(sensCap);
- LegacyCapCheck.Checked = true;
- VelocityGainCapCheck.Checked = false;
- }
- }
-
- public override void AlignActiveValues()
- {
- CapOption.AlignActiveValues();
- }
-
- void OnSensitivityCapCheckClick(object sender, EventArgs e)
- {
- if (!LegacyCapCheck.Checked)
- {
- VelocityGainCapCheck.Checked = false;
- LegacyCapCheck.Checked = true;
- }
- }
-
- void OnVelocityGainCapCheckClick(object sender, EventArgs e)
- {
- if (!VelocityGainCapCheck.Checked)
- {
- VelocityGainCapCheck.Checked = true;
- LegacyCapCheck.Checked = false;
- }
- }
-
- void OnSensitivityCapCheckedChange(object sender, EventArgs e)
- {
- if (LegacyCapCheck.Checked)
- {
- EnableSensitivityCap();
- }
- }
-
- void OnVelocityGainCapCheckedChange(object sender, EventArgs e)
- {
- if (VelocityGainCapCheck.Checked)
- {
- EnableVelocityGainCap();
- }
- }
-
- void EnableSensitivityCap()
- {
- IsSensitivityGain = true;
- CapOption.SetName("Sensitivity Cap");
- }
-
- void EnableVelocityGainCap()
- {
- IsSensitivityGain = false;
- CapOption.SetName("Velocity Gain Cap");
- }
-
- #endregion Methods
- }
-}
diff --git a/grapher/Models/Options/CheckBoxOption.cs b/grapher/Models/Options/CheckBoxOption.cs
new file mode 100644
index 0000000..abf96d3
--- /dev/null
+++ b/grapher/Models/Options/CheckBoxOption.cs
@@ -0,0 +1,109 @@
+using System.Windows.Forms;
+
+namespace grapher.Models.Options
+{
+ public class CheckBoxOption : OptionBase
+ {
+ public CheckBoxOption(
+ CheckBox checkBox,
+ ActiveValueLabel activeValueLabel)
+ {
+ CheckBox = checkBox;
+ ActiveValueLabel = activeValueLabel;
+ Show(string.Empty);
+ }
+
+ public CheckBox CheckBox { get; }
+
+ public ActiveValueLabel ActiveValueLabel { get; }
+
+ public override bool Visible
+ {
+ get
+ {
+ return CheckBox.Visible || ShouldShow;
+ }
+ }
+
+ public override int Left
+ {
+ get
+ {
+ return CheckBox.Left;
+ }
+ set
+ {
+ CheckBox.Left = value;
+ }
+ }
+
+ public override int Height
+ {
+ get
+ {
+ return CheckBox.Height;
+ }
+ }
+
+ public override int Top
+ {
+ get
+ {
+ return CheckBox.Top;
+ }
+ set
+ {
+ CheckBox.Top = value;
+ ActiveValueLabel.Top = value;
+ }
+ }
+
+ public override int Width
+ {
+ get
+ {
+ return CheckBox.Width;
+ }
+ set
+ {
+ CheckBox.Width = value;
+ }
+ }
+
+ /// <summary>
+ /// For some reason, setting CheckBox.Show() does not result in visible not being true on GUI startup.
+ /// This is inconsistent with the other options, which do.
+ /// Keep this bool for allowing Visible to still be the signal for option snapping.
+ /// </summary>
+ private bool ShouldShow { get; set; }
+
+ public override void AlignActiveValues()
+ {
+ ActiveValueLabel.Align();
+ }
+
+ public override void Hide()
+ {
+ CheckBox.Hide();
+ ShouldShow = false;
+ CheckBox.Enabled = false;
+ ActiveValueLabel.Hide();
+ }
+
+ public override void Show(string Name)
+ {
+ CheckBox.Show();
+ ShouldShow = true;
+ CheckBox.Enabled = true;
+ CheckBox.Name = Name;
+ ActiveValueLabel.Show();
+ }
+
+ public void SetActiveValue(bool legacy)
+ {
+ CheckBox.Checked = !legacy;
+ var activeValueString = legacy ? "Legacy" : "Gain";
+ ActiveValueLabel.SetValue(activeValueString);
+ }
+ }
+}
diff --git a/grapher/Models/Options/LUT/LUTPanelOptions.cs b/grapher/Models/Options/LUT/LUTPanelOptions.cs
new file mode 100644
index 0000000..6b13cdc
--- /dev/null
+++ b/grapher/Models/Options/LUT/LUTPanelOptions.cs
@@ -0,0 +1,217 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace grapher.Models.Options.LUT
+{
+ public class LUTPanelOptions : OptionBase
+ {
+ public const int PanelPadding = 5;
+ public const int PanelHeight = 100;
+
+ public LUTPanelOptions(RichTextBox pointsTextBox, RichTextBox activeValuesTextBox)
+ {
+ PointsTextBox = pointsTextBox;
+ PointsTextBox.Height = PanelHeight;
+
+ ActiveValuesTextBox = activeValuesTextBox;
+ ActiveValuesTextBox.Height = PanelHeight;
+ ActiveValuesTextBox.ReadOnly = true;
+ }
+
+ public RichTextBox PointsTextBox
+ {
+ get;
+ }
+
+ public RichTextBox ActiveValuesTextBox
+ {
+ get;
+ }
+
+ public override bool Visible
+ {
+ get
+ {
+ return PointsTextBox.Visible || ShouldShow;
+ }
+ }
+
+ public override int Top
+ {
+ get
+ {
+ return PointsTextBox.Top;
+ }
+ set
+ {
+ PointsTextBox.Top = value;
+ ActiveValuesTextBox.Top = value;
+ }
+ }
+
+ public override int Height
+ {
+ get
+ {
+ return PointsTextBox.Height;
+ }
+ }
+
+ public override int Left
+ {
+ get
+ {
+ return PointsTextBox.Left;
+ }
+ set
+ {
+ PointsTextBox.Left = value;
+ AlignActivePanelToPointsTextBox();
+ }
+ }
+
+ public override int Width
+ {
+ get
+ {
+ return PointsTextBox.Width;
+ }
+ set
+ {
+ var panelWidth = value / 2 - PanelPadding;
+ PointsTextBox.Width = panelWidth;
+ ActiveValuesTextBox.Width = panelWidth;
+ AlignActivePanelToPointsTextBox();
+ }
+ }
+
+ private bool ShouldShow { get; set; }
+
+ public override void Hide()
+ {
+ PointsTextBox.Hide();
+ ActiveValuesTextBox.Hide();
+ ShouldShow = false;
+ }
+
+ public override void Show(string name)
+ {
+ PointsTextBox.Show();
+ ActiveValuesTextBox.Show();
+ ShouldShow = true;
+ }
+
+ public override void AlignActiveValues()
+ {
+ // Nothing to do here.
+ }
+
+ public void SetActiveValues(IEnumerable<Vec2<float>> activePoints, int length)
+ {
+ if (length > 0 && activePoints.First().x != 0)
+ {
+ ActiveValuesTextBox.Text = PointsToActiveValuesText(activePoints, length);
+ }
+ else
+ {
+ ActiveValuesTextBox.Text = string.Empty;
+ }
+ }
+
+ public (Vec2<float>[], int length) GetPoints()
+ {
+ return UserTextToPoints(PointsTextBox.Text);
+ }
+
+ private static (Vec2<float>[], int length) UserTextToPoints(string userText)
+ {
+ if (string.IsNullOrWhiteSpace(userText))
+ {
+ throw new Exception("Text must be entered in text box to fill Look Up Table.");
+ }
+
+ Vec2<float>[] points = new Vec2<float>[256];
+
+ var userTextSplit = userText.Trim().Trim(';').Split(';');
+ int index = 0;
+ float lastX = 0;
+
+ foreach(var pointEntry in userTextSplit)
+ {
+ var pointSplit = pointEntry.Trim().Split(',');
+
+ if (pointSplit.Length != 2)
+ {
+ throw new Exception($"Point at index {index} is malformed. Expected format: x,y; Given: {pointEntry.Trim()}");
+ }
+
+ float x;
+ float y;
+
+ try
+ {
+ x = float.Parse(pointSplit[0]);
+ }
+ catch (Exception ex)
+ {
+ throw new Exception($"X-value for point at index {index} is malformed. Expected: float. Given: {pointSplit[0]}", ex);
+ }
+
+ if (x <= 0)
+ {
+ throw new Exception($"X-value for point at index {index} is less than or equal to 0. Point (0,0) is implied and should not be specified in points text.");
+ }
+
+ if (x <= lastX)
+ {
+ throw new Exception($"X-value for point at index {index} is less than or equal to previous x-value. Value: {x} Previous: {lastX}");
+ }
+
+ lastX = x;
+
+ try
+ {
+ y = float.Parse(pointSplit[1]);
+ }
+ catch (Exception ex)
+ {
+ throw new Exception($"Y-value for point at index {index} is malformed. Expected: float. Given: {pointSplit[1]}", ex);
+ }
+
+ if (y <= 0)
+ {
+ throw new Exception($"Y-value for point at index {index} is less than or equal to 0. Value: {y}");
+ }
+
+ points[index] = new Vec2<float> { x = x, y = y };
+
+ index++;
+ }
+
+ return (points, userTextSplit.Length);
+ }
+
+ private void AlignActivePanelToPointsTextBox()
+ {
+ ActiveValuesTextBox.Left = PointsTextBox.Right + PanelPadding;
+ }
+
+ private string PointsToActiveValuesText(IEnumerable<Vec2<float>> points, int length)
+ {
+ StringBuilder builder = new StringBuilder();
+
+ for(int i = 0; i < length; i++)
+ {
+ var point = points.ElementAt(i);
+ builder.AppendLine($"{point.x},{point.y};");
+ }
+
+ return builder.ToString();
+ }
+ }
+}
diff --git a/grapher/Models/Options/LUT/LUTPointOption.cs b/grapher/Models/Options/LUT/LUTPointOption.cs
new file mode 100644
index 0000000..6afee4f
--- /dev/null
+++ b/grapher/Models/Options/LUT/LUTPointOption.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace grapher.Models.Options.LUT
+{
+ public class LUTPointOption
+ {
+ public LUTPointOption(Form form)
+ {
+ FieldX = new Field(new System.Windows.Forms.TextBox(), form, 0);
+ FieldY = new Field(new System.Windows.Forms.TextBox(), form, 0);
+ }
+
+ public double X { get => FieldX.Data; }
+
+ public double Y { get => FieldY.Data; }
+
+ public int Top
+ {
+ get
+ {
+ return FieldX.Top;
+ }
+ set
+ {
+ FieldX.Top = value;
+ FieldY.Top = value;
+ }
+ }
+
+ private Field FieldX { get; }
+
+ private Field FieldY { get; }
+ }
+}
diff --git a/grapher/Models/Options/LUT/LutApplyOptions.cs b/grapher/Models/Options/LUT/LutApplyOptions.cs
new file mode 100644
index 0000000..7d8c737
--- /dev/null
+++ b/grapher/Models/Options/LUT/LutApplyOptions.cs
@@ -0,0 +1,183 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace grapher.Models.Options.LUT
+{
+ public class LutApplyOptions : OptionBase
+ {
+ public const string LUTApplyOptionsLabelText = "Apply as:";
+ public const int LUTApplyLabelDropdownSeparation = 4;
+
+ public enum LutApplyType
+ {
+ Sensitivity,
+ Velocity
+ }
+
+ public class LutApplyOption
+ {
+ public LutApplyType Type { get; set; }
+
+ public string Name => Type.ToString();
+
+ public override string ToString() => Name;
+ }
+
+ public static readonly LutApplyOption Sensitivity = new LutApplyOption
+ {
+ Type = LutApplyType.Sensitivity,
+ };
+
+ public static readonly LutApplyOption Velocity = new LutApplyOption
+ {
+ Type = LutApplyType.Velocity,
+ };
+
+ public LutApplyOptions(
+ Label label,
+ ComboBox applyOptionsDropdown,
+ ActiveValueLabel lutApplyActiveValue)
+ {
+ ApplyOptions = applyOptionsDropdown;
+ ApplyOptions.Items.Clear();
+ ApplyOptions.Items.AddRange(
+ new LutApplyOption[]
+ {
+ Sensitivity,
+ Velocity,
+ });
+
+ Label = label;
+ Label.Text = LUTApplyOptionsLabelText;
+ Label.AutoSize = false;
+ Label.Width = 50;
+
+ ActiveValueLabel = lutApplyActiveValue;
+ }
+
+ public LutApplyType ApplyType { get => ApplyOption.Type; }
+
+ public LutApplyOption ApplyOption {
+ get
+ {
+ return ApplyOptions.SelectedItem as LutApplyOption;
+ }
+ set
+ {
+ ApplyOptions.SelectedItem = value;
+ }
+ }
+
+ public Label Label { get; }
+
+ public ActiveValueLabel ActiveValueLabel { get; }
+
+ public ComboBox ApplyOptions { get; }
+
+ public override bool Visible
+ {
+ get
+ {
+ return Label.Visible || ShouldShow;
+ }
+ }
+
+ public override int Left
+ {
+ get
+ {
+ return Label.Left;
+ }
+ set
+ {
+ Label.Left = value;
+ ApplyOptions.Left = Label.Left + Label.Width + LUTApplyLabelDropdownSeparation;
+ }
+ }
+
+ public override int Height
+ {
+ get
+ {
+ return Label.Height;
+ }
+ }
+
+ public override int Top
+ {
+ get
+ {
+ return Label.Top;
+ }
+ set
+ {
+ ApplyOptions.Top = value;
+ Label.Top = (ApplyOptions.Height - Label.Height) / 2 + ApplyOptions.Top;
+ ActiveValueLabel.Top = value;
+ }
+ }
+
+ public override int Width
+ {
+ get
+ {
+ return Label.Width;
+ }
+ set
+ {
+ ApplyOptions.Width = value - Label.Width - Constants.OptionLabelBoxSeperation;
+ }
+ }
+
+ private bool ShouldShow { get; set; }
+
+ public override void Hide()
+ {
+ Label.Hide();
+ ApplyOptions.Hide();
+ ActiveValueLabel.Hide();
+ ShouldShow = false;
+ }
+
+ public override void Show(string labelText)
+ {
+ Label.Show();
+
+ if (!string.IsNullOrWhiteSpace(labelText))
+ {
+ Label.Text = labelText;
+ }
+
+ ApplyOptions.Show();
+ ActiveValueLabel.Show();
+ ShouldShow = true;
+ }
+
+ public override void AlignActiveValues()
+ {
+ ActiveValueLabel.Align();
+ }
+
+ public void SetActiveValue(bool applyAsVelocity)
+ {
+ ApplyOption = ApplyOptionFromSettings(applyAsVelocity);
+ ActiveValueLabel.SetValue(ApplyOption.Name);
+ }
+
+ public LutApplyOption ApplyOptionFromSettings(bool applyAsVelocity)
+ {
+ if (applyAsVelocity)
+ {
+ return Velocity;
+ }
+ else
+ {
+ return Sensitivity;
+ }
+ }
+ }
+}
diff --git a/grapher/Models/Options/OffsetOptions.cs b/grapher/Models/Options/OffsetOptions.cs
index 6638ed7..42d2d92 100644
--- a/grapher/Models/Options/OffsetOptions.cs
+++ b/grapher/Models/Options/OffsetOptions.cs
@@ -106,12 +106,9 @@ namespace grapher.Models.Options
OffsetOption.Show(name);
}
- public void SetActiveValue(double offset, bool legacy)
+ public void SetActiveValue(double offset)
{
OffsetOption.SetActiveValue(offset);
-
- VelocityGainOffsetCheck.Checked = !legacy;
- LegacyOffsetCheck.Checked = legacy;
}
public override void AlignActiveValues()
diff --git a/grapher/Models/Options/TextOption.cs b/grapher/Models/Options/TextOption.cs
new file mode 100644
index 0000000..1f8034d
--- /dev/null
+++ b/grapher/Models/Options/TextOption.cs
@@ -0,0 +1,129 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace grapher.Models.Options
+{
+ public class TextOption : OptionBase
+ {
+ #region Constants
+
+ public const string LUTLayoutExpandedText = "This mode is for experts only. Format: x1,y1;x2,y2;...xn,yn;";
+ public const string LUTLayoutShortenedText = "Experts only.";
+
+ #endregion Constants
+
+ #region Constructors
+
+ public TextOption(Label label)
+ {
+ Label = label;
+ Label.AutoSize = true;
+ }
+
+ #endregion Constructors
+
+ #region Properties
+
+ private Label Label { get; }
+
+ private string ExpandedText { get; set; }
+
+ private string ShortenedText { get; set; }
+
+ public override bool Visible
+ {
+ get
+ {
+ return Label.Visible || ShouldShow;
+ }
+ }
+
+ public override int Top
+ {
+ get
+ {
+ return Label.Top;
+ }
+ set
+ {
+ Label.Top = value;
+ }
+ }
+
+ public override int Height
+ {
+ get
+ {
+ return Label.Height;
+ }
+ }
+
+ public override int Width
+ {
+ get
+ {
+ return Label.Width;
+ }
+ set
+ {
+ Label.MaximumSize = new System.Drawing.Size(value, 0);
+ }
+ }
+
+ public override int Left
+ {
+ get
+ {
+ return Label.Left;
+ }
+ set
+ {
+ Label.Left = value;
+ }
+ }
+
+ private bool ShouldShow
+ {
+ get; set;
+ }
+
+ public override void Hide()
+ {
+ Label.Hide();
+ ShouldShow = false;
+ }
+
+ public override void Show(string Name)
+ {
+ Label.Show();
+ ShouldShow = true;
+ }
+
+ public void Expand()
+ {
+ Label.Text = ExpandedText;
+ }
+
+ public void Shorten()
+ {
+ Label.Text = ShortenedText;
+ }
+
+ public void SetText(string expandedText, string shortenedText)
+ {
+ ExpandedText = expandedText;
+ ShortenedText = shortenedText;
+ }
+
+ public override void AlignActiveValues()
+ {
+ // Nothing to do here
+ }
+
+ #endregion Properties
+ }
+}
diff --git a/grapher/Models/Serialized/GUISettings.cs b/grapher/Models/Serialized/GUISettings.cs
index a4eb627..e7b67ba 100644
--- a/grapher/Models/Serialized/GUISettings.cs
+++ b/grapher/Models/Serialized/GUISettings.cs
@@ -1,5 +1,6 @@
using Newtonsoft.Json;
using System;
+using System.IO;
namespace grapher.Models.Serialized
{
@@ -70,6 +71,32 @@ namespace grapher.Models.Serialized
StreamingMode.GetHashCode();
}
+ public void Save()
+ {
+ File.WriteAllText(Constants.GuiConfigFileName, JsonConvert.SerializeObject(this));
+ }
+
+ public static GUISettings MaybeLoad()
+ {
+ GUISettings settings = null;
+
+ try
+ {
+ settings = JsonConvert.DeserializeObject<GUISettings>(
+ File.ReadAllText(Constants.GuiConfigFileName));
+ }
+ catch (Exception ex)
+ {
+ if (!(ex is JsonException || ex is FileNotFoundException))
+ {
+ throw;
+ }
+ }
+
+ return settings;
+ }
+
#endregion Methods
+
}
}
diff --git a/grapher/Models/Serialized/RawAccelSettings.cs b/grapher/Models/Serialized/RawAccelSettings.cs
deleted file mode 100644
index 17db910..0000000
--- a/grapher/Models/Serialized/RawAccelSettings.cs
+++ /dev/null
@@ -1,141 +0,0 @@
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using System;
-using System.IO;
-using System.Linq;
-
-namespace grapher.Models.Serialized
-{
- [Serializable]
- public class RawAccelSettings
- {
- #region Fields
-
- public static readonly string ExecutingDirectory = AppDomain.CurrentDomain.BaseDirectory;
- public static readonly string DefaultSettingsFile = Path.Combine(ExecutingDirectory, Constants.DefaultSettingsFileName);
- public static readonly JsonSerializerSettings SerializerSettings = new JsonSerializerSettings
- {
- MissingMemberHandling = MissingMemberHandling.Ignore,
- };
- #endregion Fields
-
- #region Constructors
-
- public RawAccelSettings() { }
-
- public RawAccelSettings(
- DriverSettings accelSettings,
- GUISettings guiSettings)
- {
- AccelerationSettings = accelSettings;
- GUISettings = guiSettings;
- }
-
- #endregion Constructors
-
- #region Properties
- [JsonProperty(Required = Required.Always)]
- public GUISettings GUISettings { get; set; }
-
- [JsonProperty(DriverSettings.Key, Required = Required.Always)]
- public DriverSettings AccelerationSettings { get; set; }
-
- #endregion Properties
-
- #region Methods
-
- public static RawAccelSettings Load(Func<GUISettings> DefaultGUISettingsSupplier)
- {
- return Load(DefaultSettingsFile, DefaultGUISettingsSupplier);
- }
-
- public static RawAccelSettings Load(string file, Func<GUISettings> DefaultGUISettingsSupplier)
- {
- try
- {
- RawAccelSettings settings = null;
-
- JObject settingsJObject = JObject.Parse(File.ReadAllText(file));
- if (settingsJObject.ContainsKey(DriverSettings.Key))
- {
- settings = settingsJObject.ToObject<RawAccelSettings>(JsonSerializer.Create(SerializerSettings));
- }
- else
- {
- settings = new RawAccelSettings
- {
- AccelerationSettings = settingsJObject.ToObject<DriverSettings>(),
- GUISettings = DefaultGUISettingsSupplier()
- };
- }
-
- if (settings is null || settings.AccelerationSettings is null)
- {
- throw new JsonException($"{file} contains invalid JSON");
- }
-
- return settings;
- }
- catch (FileNotFoundException e)
- {
- throw new FileNotFoundException($"Settings file does not exist at {file}", e);
- }
- catch (JsonException e)
- {
- throw new JsonException($"Settings file at {file} does not contain valid Raw Accel Settings.", e);
- }
- }
-
- public static bool Exists()
- {
- return Exists(DefaultSettingsFile);
- }
-
- public static bool Exists(string file)
- {
- return File.Exists(file);
- }
-
- public void Save()
- {
- Save(DefaultSettingsFile);
- }
-
- public void Save(string file)
- {
- JObject thisJO = JObject.FromObject(this);
- AddComments(thisJO);
- File.WriteAllText(file, thisJO.ToString(Formatting.Indented));
- }
-
- private void AddComments(JObject thisJO)
- {
- string modes = string.Join(" | ", Enum.GetNames(typeof(AccelMode)));
- ((JObject)thisJO[DriverSettings.Key])
- .AddFirst(new JProperty("### Mode Types ###", modes));
- }
-
- public bool IsDefaultEquivalent()
- {
- return IsDefaultEquivalent(AccelerationSettings);
- }
-
- public static bool IsDefaultEquivalent(DriverSettings accelSettings)
- {
- bool wholeOrNoY = accelSettings.combineMagnitudes ||
- accelSettings.modes.y == AccelMode.noaccel;
-
- return string.IsNullOrEmpty(accelSettings.deviceID) &&
- accelSettings.sensitivity.x == 1 &&
- accelSettings.sensitivity.y == 1 &&
- accelSettings.directionalMultipliers.x <= 0 &&
- accelSettings.directionalMultipliers.y <= 0 &&
- accelSettings.rotation == 0 &&
- accelSettings.snap == 0 &&
- accelSettings.modes.x == AccelMode.noaccel &&
- wholeOrNoY;
- }
-
- #endregion Methods
- }
-}
diff --git a/grapher/Models/Serialized/SettingsManager.cs b/grapher/Models/Serialized/SettingsManager.cs
index c867c0a..6bcfab8 100644
--- a/grapher/Models/Serialized/SettingsManager.cs
+++ b/grapher/Models/Serialized/SettingsManager.cs
@@ -1,5 +1,6 @@
using Newtonsoft.Json;
using System;
+using System.IO;
using System.Windows.Forms;
using System.Threading;
using System.Text;
@@ -22,7 +23,6 @@ namespace grapher.Models.Serialized
ToolStripMenuItem streamingMode,
DeviceIDManager deviceIDManager)
{
- ActiveAccel = activeAccel;
DpiField = dpiField;
PollRateField = pollRateField;
AutoWriteMenuItem = autoWrite;
@@ -30,15 +30,35 @@ namespace grapher.Models.Serialized
ShowVelocityAndGainMoveMenuItem = showVelocityAndGain;
StreamingModeMenuItem = streamingMode;
DeviceIDManager = deviceIDManager;
+
+ SetActiveFields(activeAccel);
+
+ GuiSettings = GUISettings.MaybeLoad();
+
+ if (GuiSettings is null)
+ {
+ GuiSettings = MakeGUISettingsFromFields();
+ GuiSettings.Save();
+ }
+ else
+ {
+ UpdateFieldsFromGUISettings();
+ }
+
+ UserSettings = InitUserSettings();
}
#endregion Constructors
#region Properties
- public ManagedAccel ActiveAccel { get; }
+ public GUISettings GuiSettings { get; private set; }
+
+ public ManagedAccel ActiveAccel { get; private set; }
+
+ public ExtendedSettings ActiveSettings { get; private set; }
- public RawAccelSettings RawAccelSettings { get; private set; }
+ public DriverSettings UserSettings { get; private set; }
public Field DpiField { get; private set; }
@@ -55,47 +75,55 @@ namespace grapher.Models.Serialized
#endregion Properties
#region Methods
- public SettingsErrors TryUpdateActiveSettings(DriverSettings settings)
- {
- var errors = TryUpdateAccel(settings);
-
- if (errors.Empty())
- {
- RawAccelSettings.AccelerationSettings = settings;
- RawAccelSettings.GUISettings = MakeGUISettingsFromFields();
- RawAccelSettings.Save();
- }
- return errors;
+ public void DisableDriver()
+ {
+ var defaultSettings = new ExtendedSettings();
+ ActiveSettings = defaultSettings;
+ ActiveAccel.Settings = defaultSettings;
+ new Thread(() => ActiveAccel.Activate()).Start();
}
public void UpdateFieldsFromGUISettings()
{
- DpiField.SetToEntered(RawAccelSettings.GUISettings.DPI);
- PollRateField.SetToEntered(RawAccelSettings.GUISettings.PollRate);
- ShowLastMouseMoveMenuItem.Checked = RawAccelSettings.GUISettings.ShowLastMouseMove;
- ShowVelocityAndGainMoveMenuItem.Checked = RawAccelSettings.GUISettings.ShowVelocityAndGain;
- StreamingModeMenuItem.Checked = RawAccelSettings.GUISettings.StreamingMode;
- AutoWriteMenuItem.Checked = RawAccelSettings.GUISettings.AutoWriteToDriverOnStartup;
+ DpiField.SetToEntered(GuiSettings.DPI);
+ PollRateField.SetToEntered(GuiSettings.PollRate);
+ ShowLastMouseMoveMenuItem.Checked = GuiSettings.ShowLastMouseMove;
+ ShowVelocityAndGainMoveMenuItem.Checked = GuiSettings.ShowVelocityAndGain;
+ StreamingModeMenuItem.Checked = GUISettings.StreamingMode;
+ AutoWriteMenuItem.Checked = GuiSettings.AutoWriteToDriverOnStartup;
}
- public SettingsErrors TryUpdateAccel(DriverSettings settings)
+ public SettingsErrors TryActivate(DriverSettings settings)
{
- var errors = SendToDriverSafe(settings);
- if (errors.Empty()) ActiveAccel.UpdateFromSettings(settings);
- return errors;
- }
+ var errors = new SettingsErrors(settings);
- public static void SendToDriver(DriverSettings settings)
- {
- new Thread(() => DriverInterop.Write(settings)).Start();
+ if (errors.Empty())
+ {
+ GuiSettings = MakeGUISettingsFromFields();
+ GuiSettings.Save();
+
+ UserSettings = settings;
+ File.WriteAllText(Constants.DefaultSettingsFileName, RaConvert.Settings(settings));
+
+ ActiveSettings = new ExtendedSettings(settings);
+ ActiveAccel.Settings = ActiveSettings;
+
+ new Thread(() => ActiveAccel.Activate()).Start();
+ }
+
+ return errors;
}
- public static SettingsErrors SendToDriverSafe(DriverSettings settings)
+ public void SetHiddenOptions(DriverSettings settings)
{
- var errors = DriverInterop.GetSettingsErrors(settings);
- if (errors.Empty()) SendToDriver(settings);
- return errors;
+ settings.snap = UserSettings.snap;
+ settings.maximumSpeed = UserSettings.maximumSpeed;
+ settings.minimumSpeed = UserSettings.minimumSpeed;
+ settings.minimumTime = UserSettings.minimumTime;
+ settings.maximumTime = UserSettings.maximumTime;
+ settings.ignore = UserSettings.ignore;
+ settings.directionalMultipliers = UserSettings.directionalMultipliers;
}
public GUISettings MakeGUISettingsFromFields()
@@ -111,32 +139,51 @@ namespace grapher.Models.Serialized
};
}
- // Returns true when file settings are active
- public bool Startup()
+ public bool TableActive()
+ {
+ return ActiveSettings.tables.x != null || ActiveSettings.tables.y != null;
+ }
+
+ public void SetActiveFields(ManagedAccel activeAccel)
{
- if (RawAccelSettings.Exists())
+ ActiveAccel = activeAccel;
+ ActiveSettings = activeAccel.Settings;
+ }
+
+ private DriverSettings InitUserSettings()
+ {
+ var path = Constants.DefaultSettingsFileName;
+ if (File.Exists(path))
{
try
{
- RawAccelSettings = RawAccelSettings.Load(() => MakeGUISettingsFromFields());
- UpdateFieldsFromGUISettings();
- if (RawAccelSettings.GUISettings.AutoWriteToDriverOnStartup)
+ DriverSettings settings = RaConvert.Settings(File.ReadAllText(path));
+
+ if (!GuiSettings.AutoWriteToDriverOnStartup ||
+ TableActive() ||
+ TryActivate(settings).Empty())
{
- TryUpdateAccel(RawAccelSettings.AccelerationSettings);
+ return settings;
}
- return RawAccelSettings.GUISettings.AutoWriteToDriverOnStartup;
+
}
catch (JsonException e)
{
- Console.WriteLine($"bad settings: {e}");
+ System.Diagnostics.Debug.WriteLine($"bad settings: {e}");
}
}
- RawAccelSettings = new RawAccelSettings(
- DriverInterop.GetActiveSettings(),
- MakeGUISettingsFromFields());
- RawAccelSettings.Save();
- return true;
+ if (!TableActive())
+ {
+ File.WriteAllText(path, RaConvert.Settings(ActiveSettings.baseSettings));
+ return ActiveSettings.baseSettings;
+ }
+ else
+ {
+ var defaultSettings = new DriverSettings();
+ File.WriteAllText(path, RaConvert.Settings(defaultSettings));
+ return defaultSettings;
+ }
}
#endregion Methods
diff --git a/grapher/Properties/AssemblyInfo.cs b/grapher/Properties/AssemblyInfo.cs
index a46d6b3..d381102 100644
--- a/grapher/Properties/AssemblyInfo.cs
+++ b/grapher/Properties/AssemblyInfo.cs
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion(RawAccelVersion.value)]
-[assembly: AssemblyFileVersion(RawAccelVersion.value)]
+[assembly: AssemblyVersion(VersionHelper.VersionString)]
+[assembly: AssemblyFileVersion(VersionHelper.VersionString)]
diff --git a/grapher/grapher.csproj b/grapher/grapher.csproj
index 1160ba8..7b0ab02 100644
--- a/grapher/grapher.csproj
+++ b/grapher/grapher.csproj
@@ -78,8 +78,10 @@
<DependentUpon>AboutBox.cs</DependentUpon>
</Compile>
<Compile Include="Constants\Constants.cs" />
+ <Compile Include="Layouts\LUTLayout.cs" />
<Compile Include="Layouts\MotivityLayout.cs" />
- <Compile Include="Layouts\NaturalGainLayout.cs" />
+ <Compile Include="Layouts\JumpLayout.cs" />
+ <Compile Include="Layouts\UnsupportedLayout.cs" />
<Compile Include="MessageDialog.cs">
<SubType>Form</SubType>
</Compile>
@@ -111,7 +113,6 @@
<Compile Include="Models\Options\ActiveValueLabel.cs" />
<Compile Include="Models\Options\ActiveValueLabelXY.cs" />
<Compile Include="Models\Options\ApplyOptions.cs" />
- <Compile Include="Models\Options\CapOptions.cs" />
<Compile Include="Models\Charts\ChartXY.cs" />
<Compile Include="Models\Fields\Field.cs" />
<Compile Include="Models\Fields\FieldXY.cs" />
@@ -128,15 +129,18 @@
<Compile Include="Layouts\NaturalLayout.cs" />
<Compile Include="Layouts\OffLayout.cs" />
<Compile Include="Layouts\PowerLayout.cs" />
+ <Compile Include="Models\Options\CheckBoxOption.cs" />
<Compile Include="Models\Options\Directionality\DirectionalityOptions.cs" />
+ <Compile Include="Models\Options\LUT\LutApplyOptions.cs" />
<Compile Include="Models\Options\IOption.cs" />
+ <Compile Include="Models\Options\LUT\LUTPanelOptions.cs" />
<Compile Include="Models\Options\OffsetOptions.cs" />
<Compile Include="Models\Options\Option.cs" />
<Compile Include="Layouts\OptionLayout.cs" />
<Compile Include="Models\Options\OptionBase.cs" />
<Compile Include="Models\Options\OptionXY.cs" />
+ <Compile Include="Models\Options\TextOption.cs" />
<Compile Include="Models\Serialized\GUISettings.cs" />
- <Compile Include="Models\Serialized\RawAccelSettings.cs" />
<Compile Include="Models\Serialized\SettingsManager.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
diff --git a/signed/driver/rawaccel.sys b/signed/driver/rawaccel.sys
index 653d927..b399578 100644
--- a/signed/driver/rawaccel.sys
+++ b/signed/driver/rawaccel.sys
Binary files differ
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 50a3596..6344b77 100644
--- a/wrapper/wrapper.cpp
+++ b/wrapper/wrapper.cpp
@@ -1,51 +1,53 @@
#pragma once
-#include <type_traits>
-#include <msclr\marshal_cppstd.h>
+#include "interop-exception.h"
-#include <rawaccel.hpp>
-#include <rawaccel-version.h>
-#include <utility-rawinput.hpp>
-
-#include "wrapper_io.hpp"
+#include <rawaccel-io.hpp>
+#include <rawaccel-validate.hpp>
using namespace System;
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 Windows::Forms;
+namespace ra = rawaccel;
-using namespace Newtonsoft::Json;
+ra::settings default_settings;
[JsonConverter(Converters::StringEnumConverter::typeid)]
public enum class AccelMode
{
- linear, classic, natural, naturalgain, power, motivity, noaccel
+ classic, jump, natural, motivity, power, lut, noaccel
+};
+
+[JsonConverter(Converters::StringEnumConverter::typeid)]
+public enum class SpacedTableMode
+{
+ off, binlog, linear
};
-[JsonObject(ItemRequired = Required::Always)]
[StructLayout(LayoutKind::Sequential)]
-public value struct AccelArgs
+public value struct SpacedTableArgs
{
- double offset;
+ [JsonIgnore]
+ SpacedTableMode mode;
+
[MarshalAs(UnmanagedType::U1)]
- bool legacyOffset;
- double acceleration;
- double scale;
- double limit;
- double exponent;
- double midpoint;
- double weight;
- [JsonProperty("legacyCap")]
- double scaleCap;
- double gainCap;
- [JsonProperty(Required = Required::Default)]
- double speedCap;
+ bool transfer;
+
+ [MarshalAs(UnmanagedType::U1)]
+ unsigned char partitions;
+
+ short num;
+ double start;
+ double stop;
};
+
generic <typename T>
-[JsonObject(ItemRequired = Required::Always)]
[StructLayout(LayoutKind::Sequential)]
public value struct Vec2
{
@@ -53,7 +55,78 @@ public value struct Vec2
T y;
};
-[JsonObject(ItemRequired = Required::Always)]
+[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
+{
+ AccelMode mode;
+
+ [MarshalAs(UnmanagedType::U1)]
+ bool legacy;
+
+ double offset;
+ double cap;
+ double accelClassic;
+ double decayRate;
+ double growthRate;
+ double motivity;
+ double power;
+ double scale;
+ double weight;
+ double exponent;
+ double limit;
+ double midpoint;
+ double smooth;
+
+ [JsonProperty(Required = Required::Default)]
+ SpacedTableArgs spacedTableArgs;
+
+ TableArgs tableData;
+};
+
[StructLayout(LayoutKind::Sequential)]
public value struct DomainArgs
{
@@ -65,20 +138,25 @@ public value struct DomainArgs
[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", Required = Required::Default)]
+ [JsonProperty("Degrees of angle snapping")]
double snap;
[JsonProperty("Use x as whole/combined accel")]
[MarshalAs(UnmanagedType::U1)]
bool combineMagnitudes;
- [JsonProperty("Accel modes")]
- Vec2<AccelMode> modes;
+ double dpi;
+
+ [JsonIgnore]
+ double minimumSpeed;
+ [JsonProperty("Input Speed Cap")]
+ double maximumSpeed;
[JsonProperty("Accel parameters")]
Vec2<AccelArgs> args;
@@ -86,302 +164,387 @@ public ref struct DriverSettings
[JsonProperty("Sensitivity multipliers")]
Vec2<double> sensitivity;
- [JsonProperty("Negative directional multipliers", Required = Required::Default)]
+ [JsonProperty("Negative directional multipliers")]
Vec2<double> directionalMultipliers;
- [JsonProperty("Stretches domain for horizontal vs vertical inputs", Required = Required::Default)]
+ [JsonProperty("Stretches domain for horizontal vs vertical inputs")]
DomainArgs domainArgs;
- [JsonProperty("Stretches accel range for horizontal vs vertical inputs", Required = Required::Default)]
+ [JsonProperty("Stretches accel range for horizontal vs vertical inputs")]
Vec2<double> rangeXY;
[JsonProperty(Required = Required::Default)]
double minimumTime;
- [JsonProperty("Device ID", Required = Required::Default)]
- [MarshalAs(UnmanagedType::ByValTStr, SizeConst = MAX_DEV_ID_LEN)]
+ [JsonProperty(Required = Required::Default)]
+ double maximumTime;
+
+ [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 = "";
bool ShouldSerializeminimumTime()
{
- return minimumTime > 0 && minimumTime != DEFAULT_TIME_MIN;
+ return minimumTime != ra::DEFAULT_TIME_MIN;
+ }
+
+ bool ShouldSerializemaximumTime()
+ {
+ return maximumTime != ra::DEFAULT_TIME_MAX;
}
DriverSettings()
{
- domainArgs = { { 1, 1 }, 2 };
- rangeXY = { 1, 1 };
+ Marshal::PtrToStructure(IntPtr(&default_settings), this);
}
-};
+private:
+ [OnDeserialized]
+ void OnDeserializedMethod(StreamingContext context)
+ {
+ args.x.tableData.length = args.x.tableData.points->Length;
+ args.y.tableData.length = args.y.tableData.points->Length;
-template <typename NativeSettingsFunc>
-void as_native(DriverSettings^ managed_args, NativeSettingsFunc fn)
-{
-#ifndef NDEBUG
- if (Marshal::SizeOf(managed_args) != sizeof(settings))
- throw gcnew InvalidOperationException("setting sizes differ");
-#endif
- settings args;
- Marshal::StructureToPtr(managed_args, (IntPtr)&args, false);
- fn(args);
- if constexpr (!std::is_invocable_v<NativeSettingsFunc, const settings&>) {
- Marshal::PtrToStructure((IntPtr)&args, managed_args);
- }
-}
-
-DriverSettings^ get_default()
-{
- DriverSettings^ managed = gcnew DriverSettings();
- as_native(managed, [](settings& args) {
- args = {};
- });
- return managed;
-}
-
-void set_active(DriverSettings^ managed)
-{
- as_native(managed, [](const settings& args) {
- wrapper_io::writeToDriver(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);
+ }
-DriverSettings^ get_active()
-{
- DriverSettings^ managed = gcnew DriverSettings();
- as_native(managed, [](settings& args) {
- wrapper_io::readFromDriver(args);
- });
- return managed;
-}
-
-void update_modifier(mouse_modifier& mod, DriverSettings^ managed, vec2<si_pair*> luts = {})
-{
- as_native(managed, [&](const settings& args) {
- mod = { args, luts };
- });
-}
+ [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);
+ }
+
+ [OnSerialized]
+ void OnSerializedMethod(StreamingContext context)
+ {
+ array<Vec2<float>>::Resize(args.x.tableData.points, ra::ARB_LUT_CAPACITY);
+ array<Vec2<float>>::Resize(args.y.tableData.points, ra::ARB_LUT_CAPACITY);
+ }
-using error_list_t = Collections::Generic::List<String^>;
+};
-error_list_t^ get_accel_errors(AccelMode mode, AccelArgs^ args)
+[JsonObject(ItemRequired = Required::Always)]
+public ref struct LutBase
{
- accel_mode mode_native = (accel_mode)mode;
-
- auto is_mode = [mode_native](auto... modes) {
- return ((mode_native == modes) || ...);
- };
-
- using am = accel_mode;
+ [JsonConverter(Converters::StringEnumConverter::typeid)]
+ enum class Mode
+ {
+ logarithmic, linear
+ } mode;
- auto error_list = gcnew error_list_t();
-
- if (args->acceleration > 10 && is_mode(am::natural, am::naturalgain))
- error_list->Add("acceleration can not be greater than 10");
- else if (args->acceleration == 0 && is_mode(am::naturalgain))
- error_list->Add("acceleration must be positive");
- else if (args->acceleration < 0) {
- bool additive = mode_native < am::power;
- if (additive) error_list->Add("acceleration can not be negative, use a negative weight to compensate");
- else error_list->Add("acceleration can not be negative");
- }
-
- if (args->scale <= 0)
- error_list->Add("scale must be positive");
+ virtual void SetArgs(AccelArgs%) {}
+ virtual void SetData(ra::accel_union&) {}
+};
- if (args->exponent <= 1 && is_mode(am::classic))
- error_list->Add("exponent must be greater than 1");
- else if (args->exponent <= 0)
- error_list->Add("exponent must be positive");
- if (args->limit <= 1)
- error_list->Add("limit must be greater than 1");
+[JsonObject(ItemRequired = Required::Always)]
+public ref struct SpacedLut abstract : public LutBase
+{
+ [JsonProperty("Whether points affect velocity (true) or sensitivity (false)")]
+ bool transfer;
- if (args->midpoint <= 0)
- error_list->Add("midpoint must be positive");
+ double start;
+ double stop;
+ array<float>^ data;
- if (args->offset < 0)
- error_list->Add("offset can not be negative");
+ void SetArgsBase(AccelArgs% args)
+ {
+ args.spacedTableArgs.transfer = transfer;
+ args.spacedTableArgs.start = start;
+ args.spacedTableArgs.stop = stop;
+ }
- return error_list;
-}
+ void SetDataBase(ra::accel_union& accel)
+ {
+ if (size_t(data->LongLength) > ra::SPACED_LUT_CAPACITY) {
+ throw gcnew InteropException("data is too large");
+ }
+ }
+};
-error_list_t^ get_other_errors(DriverSettings^ settings)
+[JsonObject(ItemRequired = Required::Always)]
+public ref struct LinearLut sealed : public SpacedLut
{
- auto error_list = gcnew error_list_t();
+ LinearLut()
+ {
+ }
- if (settings->rangeXY.x <= 0 || settings->rangeXY.y <= 0)
+ LinearLut(const ra::linear_lut& table)
{
- error_list->Add("range values must be positive");
+ 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->domainArgs.domainXY.x <= 0 || settings->domainArgs.domainXY.y <= 0)
+ virtual void SetArgs(AccelArgs% args) override
{
- error_list->Add("domain values must be positive");
+ SetArgsBase(args);
+ args.spacedTableArgs.num = data->Length;
+ args.spacedTableArgs.mode = SpacedTableMode::linear;
}
- if (settings->domainArgs.lpNorm <= 0)
+ virtual void SetData(ra::accel_union& accel) override
{
- error_list->Add("lp norm must be positive");
+ SetDataBase(accel);
+ pin_ptr<float> pdata = &data[0];
+ std::memcpy(&accel.lin_lut.data, pdata, sizeof(float) * data->Length);
}
-
- return error_list;
-}
+};
-public ref class SettingsErrors
+[JsonObject(ItemRequired = Required::Always)]
+public ref struct BinLogLut sealed : public SpacedLut
{
-public:
- error_list_t^ x;
- error_list_t^ y;
- error_list_t^ other;
+ short num;
- bool Empty()
+ BinLogLut()
{
- return (x == nullptr || x->Count == 0) &&
- (y == nullptr || y->Count == 0) &&
- (other == nullptr || other->Count == 0);
}
- virtual String^ ToString() override
+ BinLogLut(const ra::binlog_lut& table)
{
- if (x == nullptr) throw;
+ 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);
+ }
- Text::StringBuilder^ sb = gcnew Text::StringBuilder();
+ virtual void SetArgs(AccelArgs% args) override
+ {
+ SetArgsBase(args);
+ args.spacedTableArgs.num = num;
+ args.spacedTableArgs.mode = SpacedTableMode::binlog;
+ }
- if (y == nullptr) // assume combineMagnitudes
- {
- for each (String^ str in x)
- {
- sb->AppendLine(str);
- }
- }
- else
- {
- for each (String^ str in x)
- {
- sb->AppendFormat("x: {0}\n", str);
- }
- for each (String^ str in y)
- {
- sb->AppendFormat("y: {0}\n", str);
- }
- }
+ virtual void SetData(ra::accel_union& accel) override
+ {
+ SetDataBase(accel);
- for each (String ^ str in other)
- {
- sb->AppendLine(str);
+ if (data->Length != 1 + num * (int(stop) - int(start))) {
+ throw gcnew InteropException("size of data does not match args");
}
-
- return sb->ToString();
+
+ pin_ptr<float> pdata = &data[0];
+ std::memcpy(&accel.log_lut.data, pdata, sizeof(float) * data->Length);
}
};
-public ref struct RawInputInterop
-{
- static void AddHandlesFromID(String^ deviceID, List<IntPtr>^ rawInputHandles)
+public ref struct RaConvert {
+
+ static DriverSettings^ Settings(String^ json)
{
- try
- {
- std::vector<HANDLE> nativeHandles = rawinput_handles_from_dev_id(
- msclr::interop::marshal_as<std::wstring>(deviceID));
+ 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);
- for (auto nh : nativeHandles) rawInputHandles->Add(IntPtr(nh));
+ if ((Object^)jObject == nullptr) {
+ throw gcnew JsonException("bad json");
}
- catch (const std::exception& e)
- {
- throw gcnew System::Exception(gcnew String(e.what()));
+
+ 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();
}
}
- static List<String^>^ GetDeviceIDs()
+ static String^ Table(LutBase^ lut)
{
- try
- {
- auto managed = gcnew List<String^>();
+ auto serializerSettings = gcnew JsonSerializerSettings();
+ return JsonConvert::SerializeObject(
+ lut, lut->GetType(), Formatting::Indented, serializerSettings);
+ };
- for (auto&& id : rawinput_dev_id_list())
- {
- managed->Add(msclr::interop::marshal_as<String^>(id));
- }
+ 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) {}
- return managed;
+ 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);
}
- catch (const std::exception& e)
- {
- throw gcnew System::Exception(gcnew String(e.what()));
+ else {
+ baseSettings = gcnew DriverSettings();
+ }
+
+ if (xTableJson || yTableJson) {
+ baseSettings->combineMagnitudes = !byComponent;
+ }
+
+ if (xTableJson) {
+ tables.x = RaConvert::Table(xTableJson);
+ tables.x->SetArgs(baseSettings->args.x);
+ }
+
+ if (yTableJson) {
+ if (Object::ReferenceEquals(yTableJson, xTableJson)) {
+ tables.y = tables.x;
+ }
+ else {
+ tables.y = RaConvert::Table(yTableJson);
+ }
+
+ tables.y->SetArgs(baseSettings->args.y);
}
}
};
-public ref struct DriverInterop
+public ref class SettingsErrors
{
- literal double WriteDelayMs = WRITE_DELAY;
- static initonly DriverSettings^ DefaultSettings = get_default();
+public:
- static DriverSettings^ GetActiveSettings()
- {
- return get_active();
- }
+ List<String^>^ list;
+ int countX;
+ int countY;
- static void Write(DriverSettings^ args)
- {
- set_active(args);
- }
+ delegate void MsgHandler(const char*);
- static DriverSettings^ GetDefaultSettings()
+ void Add(const char* msg)
{
- return get_default();
+ list->Add(gcnew String(msg));
}
- static SettingsErrors^ GetSettingsErrors(DriverSettings^ args)
+ SettingsErrors(ExtendedSettings^ settings) :
+ SettingsErrors(settings->baseSettings) {}
+
+ SettingsErrors(DriverSettings^ settings)
{
- auto errors = gcnew SettingsErrors();
+ MsgHandler^ del = gcnew MsgHandler(this, &SettingsErrors::Add);
+ GCHandle gch = GCHandle::Alloc(del);
+ auto fp = static_cast<void (*)(const char*)>(
+ Marshal::GetFunctionPointerForDelegate(del).ToPointer());
- errors->x = get_accel_errors(args->modes.x, args->args.x);
+ ra::settings* args_ptr = new ra::settings();
+ Marshal::StructureToPtr(settings, (IntPtr)args_ptr, false);
- if (!args->combineMagnitudes) {
- errors->y = get_accel_errors(args->modes.y, args->args.y);
- }
+ list = gcnew List<String^>();
+ auto [cx, cy, _] = ra::valid(*args_ptr, fp);
+ countX = cx;
+ countY = cy;
- errors->other = get_other_errors(args);
+ gch.Free();
+ delete args_ptr;
+ }
- return errors;
+ 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);
+ }
- static error_list_t^ GetAccelErrors(AccelMode mode, AccelArgs^ args)
- {
- return get_accel_errors(mode, args);
+ return sb->ToString();
}
};
+struct instance_t {
+ ra::io_t data;
+ vec2<ra::accel_invoker> inv;
+};
+
public ref class ManagedAccel
{
- mouse_modifier* const modifier_instance = new mouse_modifier();
-#ifdef RA_LOOKUP
- si_pair* const lut_x = new si_pair[LUT_SIZE];
- si_pair* const lut_y = new si_pair[LUT_SIZE];
-#else
- si_pair* lut_x = nullptr;
- si_pair* lut_y = nullptr;
-#endif
+ instance_t* const instance = new instance_t();
public:
+ ManagedAccel() {}
+
+ ManagedAccel(ExtendedSettings^ settings)
+ {
+ Settings = settings;
+ }
virtual ~ManagedAccel()
{
- delete modifier_instance;
- delete[] lut_x;
- delete[] lut_y;
+ delete instance;
}
!ManagedAccel()
{
- delete modifier_instance;
- delete[] lut_x;
- delete[] lut_y;
+ delete instance;
}
Tuple<double, double>^ Accelerate(int x, int y, double time)
@@ -390,90 +553,89 @@ public:
(double)x,
(double)y
};
- modifier_instance->modify(in_out_vec, time);
+
+ instance->data.mod.modify(in_out_vec, instance->inv, time);
return gcnew Tuple<double, double>(in_out_vec.x, in_out_vec.y);
}
- void UpdateFromSettings(DriverSettings^ args)
+ void Activate()
{
- update_modifier(
- *modifier_instance,
- args,
- vec2<si_pair*>{ lut_x, lut_y }
- );
+ try {
+ ra::write(instance->data);
+ }
+ catch (const ra::error& e) {
+ throw gcnew InteropException(e);
+ }
}
- static ManagedAccel^ GetActiveAccel()
+ property ExtendedSettings^ Settings
{
- settings args;
- wrapper_io::readFromDriver(args);
-
- auto active = gcnew ManagedAccel();
- *active->modifier_instance = {
- args
- , vec2<si_pair*> { active->lut_x, active->lut_y }
- };
- return active;
- }
-};
-
-public ref struct RawAccelVersion
-{
- literal String^ value = RA_VER_STRING;
-};
-
-public ref struct VersionException : public Exception
-{
-public:
- VersionException() {}
- VersionException(String^ what) : Exception(what) {}
-};
-
-Version^ convert(rawaccel::version_t v)
-{
- return gcnew Version(v.major, v.minor, v.patch, 0);
-}
+ 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;
+ }
-public ref struct VersionHelper
-{
+ 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);
- static Version^ ValidateAndGetDriverVersion(Version^ wrapperTarget)
- {
- Version^ wrapperActual = VersionHelper::typeid->Assembly->GetName()->Version;
+ if (val->tables.x) {
+ val->tables.x->SetData(instance->data.mod.accel.x);
+ }
- if (wrapperTarget != wrapperActual) {
- throw gcnew VersionException("version mismatch, expected wrapper.dll v" + wrapperActual);
+ if (val->tables.y) {
+ val->tables.y->SetData(instance->data.mod.accel.y);
+ }
}
- version_t drv_ver;
+ }
+ static ManagedAccel^ GetActive()
+ {
try {
- wrapper_io::getDriverVersion(drv_ver);
+ auto active = gcnew ManagedAccel();
+ ra::read(active->instance->data);
+ active->instance->inv = ra::invokers(active->instance->data.args);
+ return active;
}
- catch (DriverNotInstalledException^ ex) {
- throw gcnew VersionException(ex->Message);
+ catch (const ra::error& e) {
+ throw gcnew InteropException(e);
}
- catch (DriverIOException^) {
- // Assume version ioctl is unimplemented (driver version < v1.3.0)
- throw gcnew VersionException("driver version is out of date\n\nrun installer.exe to reinstall");
+ }
+
+private:
+ LutBase^ extract(ra::spaced_lut_mode mode, ra::accel_union& au)
+ {
+ 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();
}
+ }
+};
- Version^ drv_ver_managed = convert(drv_ver);
+public ref struct VersionHelper
+{
+ literal String^ VersionString = RA_VER_STRING;
- if (drv_ver_managed < convert(min_driver_version)) {
- throw gcnew VersionException(
- String::Format("driver version is out of date (v{0})\n\nrun installer.exe to reinstall",
- drv_ver_managed));
- }
- else if (drv_ver_managed > wrapperActual) {
- throw gcnew VersionException(
- String::Format("newer driver version is installed (v{0})",
- drv_ver_managed));
+ static Version^ ValidOrThrow()
+ {
+ try {
+ ra::version_t v = ra::valid_version_or_throw();
+ return gcnew Version(v.major, v.minor, v.patch, 0);
}
- else {
- return drv_ver_managed;
+ catch (const ra::error& e) {
+ throw gcnew InteropException(e);
}
}
-
};
diff --git a/wrapper/wrapper.vcxproj b/wrapper/wrapper.vcxproj
index 721a981..cd121a2 100644
--- a/wrapper/wrapper.vcxproj
+++ b/wrapper/wrapper.vcxproj
@@ -57,6 +57,7 @@
<WarningLevel>Level3</WarningLevel>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<LanguageStandard>stdcpp17</LanguageStandard>
+ <FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<AdditionalDependencies>User32.lib;</AdditionalDependencies>
@@ -70,6 +71,7 @@
<WarningLevel>Level3</WarningLevel>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<LanguageStandard>stdcpp17</LanguageStandard>
+ <FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<AdditionalDependencies>User32.lib;</AdditionalDependencies>
@@ -83,19 +85,18 @@ copy /Y "$(TargetDir)Newtonsoft.Json.dll" "$(SolutionDir)signed\Newtonsoft.Json.
</ResourceCompile>
</ItemDefinitionGroup>
<ItemGroup>
+ <ClInclude Include="interop-exception.h" />
<ClInclude Include="resource.h" />
- <ClInclude Include="wrapper_io.hpp" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="AssemblyInfo.cpp" />
+ <ClCompile Include="input.cpp" />
<ClCompile Include="wrapper.cpp" />
- <ClCompile Include="wrapper_io.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 f265d72..9caeae1 100644
--- a/wrapper/wrapper.vcxproj.filters
+++ b/wrapper/wrapper.vcxproj.filters
@@ -15,10 +15,10 @@
</Filter>
</ItemGroup>
<ItemGroup>
- <ClInclude Include="wrapper_io.hpp">
+ <ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="resource.h">
+ <ClInclude Include="interop-exception.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
@@ -26,10 +26,10 @@
<ClCompile Include="wrapper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="wrapper_io.cpp">
+ <ClCompile Include="AssemblyInfo.cpp">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="AssemblyInfo.cpp">
+ <ClCompile Include="input.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
diff --git a/wrapper/wrapper_io.cpp b/wrapper/wrapper_io.cpp
deleted file mode 100644
index 4b77174..0000000
--- a/wrapper/wrapper_io.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-#pragma once
-
-#include <rawaccel-io.hpp>
-#include "wrapper_io.hpp"
-
-auto with_managed_ex = [](auto fn) {
- try
- {
- fn();
- }
- catch (const install_error&)
- {
- throw gcnew DriverNotInstalledException();
- }
- catch (const std::system_error& e)
- {
- throw gcnew DriverIOException(gcnew String(e.what()));
- }
-};
-
-void wrapper_io::writeToDriver(const settings& args)
-{
- with_managed_ex([&] {
- write(args);
- });
-}
-
-void wrapper_io::readFromDriver(settings& args)
-{
- with_managed_ex([&] {
- args = read();
- });
-}
-
-void wrapper_io::getDriverVersion(version_t& ver)
-{
- with_managed_ex([&] {
- ver = get_version();
- });
-}
diff --git a/wrapper/wrapper_io.hpp b/wrapper/wrapper_io.hpp
deleted file mode 100644
index 6b94e46..0000000
--- a/wrapper/wrapper_io.hpp
+++ /dev/null
@@ -1,25 +0,0 @@
-#pragma once
-
-#include <rawaccel-error.hpp>
-#include <rawaccel-settings.h>
-#include <rawaccel-version.h>
-
-using namespace rawaccel;
-using namespace System;
-
-struct wrapper_io {
- static void writeToDriver(const settings&);
- static void readFromDriver(settings&);
- static void getDriverVersion(version_t&);
-};
-
-public ref struct DriverIOException : public IO::IOException {
-public:
- DriverIOException() {}
- DriverIOException(String^ what) : IO::IOException(what) {}
-};
-
-public ref struct DriverNotInstalledException : public DriverIOException {
- DriverNotInstalledException() :
- DriverIOException(gcnew String(install_error().what())) {}
-};
diff --git a/writer/Program.cs b/writer/Program.cs
index 37e384c..6cbcf8e 100644
--- a/writer/Program.cs
+++ b/writer/Program.cs
@@ -1,8 +1,9 @@
using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
using System;
+using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Windows.Forms;
namespace writer
@@ -11,68 +12,109 @@ namespace writer
class Program
{
- static void Show(string msg)
+ static void ExitWithMessage(string msg)
{
MessageBox.Show(msg, "Raw Accel writer");
+ Environment.Exit(1);
}
- static void Send(JToken settingsToken)
+ static void ExitWithUsage()
{
- var settings = settingsToken.ToObject<DriverSettings>();
+ ExitWithMessage($"Usage: {System.AppDomain.CurrentDomain.FriendlyName} <settings file path>");
+ }
- var errors = DriverInterop.GetSettingsErrors(settings);
- if (errors.Empty())
- {
- DriverInterop.Write(settings);
- return;
- }
+ delegate string PopOption(params string[] aliases);
- Show($"Bad settings:\n\n{errors}");
+ static string Read(string path)
+ {
+ return path == null ? null : File.ReadAllText(path);
}
- static void Main(string[] args)
+ static ExtendedSettings Parse(List<string> args)
{
- try
+ PopOption maybePop = aliases =>
{
- VersionHelper.ValidateAndGetDriverVersion(typeof(Program).Assembly.GetName().Version);
- }
- catch (VersionException e)
+ int idx = args.FindIndex(aliases.Contains);
+
+ if (idx == -1) return null;
+
+ if (idx == args.Count - 1) ExitWithUsage();
+
+ string val = args[idx + 1];
+ args.RemoveRange(idx, 2);
+ return val;
+ };
+
+ string settingsPath = null;
+
+ string tablePath = maybePop("table", "t");
+
+ if (tablePath != null)
{
- Show(e.Message);
- return;
+ if (args.Count > 1) ExitWithUsage();
+ else if (args.Count == 1) settingsPath = args[0];
+
+ return new ExtendedSettings(Read(settingsPath), Read(tablePath));
}
- if (args.Length != 1)
+ string xTablePath = maybePop("xtable", "xt");
+ string yTablePath = maybePop("ytable", "yt");
+
+ if (args.Count > 1) ExitWithUsage();
+ else if (args.Count == 1) settingsPath = args[0];
+ else if (xTablePath == null && yTablePath == null) ExitWithUsage();
+
+ string xTableJson = Read(xTablePath);
+ string yTableJson = null;
+
+ if (xTablePath != null && xTablePath.Equals(yTablePath))
+ {
+ yTableJson = xTableJson;
+ }
+ else
{
- Show($"Usage: {System.AppDomain.CurrentDomain.FriendlyName} <settings file path>");
- return;
+ yTableJson = Read(yTablePath);
}
- if (!File.Exists(args[0]))
+ return new ExtendedSettings(Read(settingsPath), xTableJson, yTableJson);
+ }
+
+ static void Main(string[] args)
+ {
+ try
{
- Show($"Settings file not found at {args[0]}");
- return;
+ VersionHelper.ValidOrThrow();
+ }
+ catch (InteropException e)
+ {
+ ExitWithMessage(e.Message);
}
try
{
- var JO = JObject.Parse(File.ReadAllText(args[0]));
+ var settings = Parse(new List<string>(args));
+ var errors = new SettingsErrors(settings);
- if (JO.ContainsKey(DriverSettings.Key))
+ if (errors.Empty())
{
- Send(JO[DriverSettings.Key]);
- return;
+ new ManagedAccel(settings).Activate();
}
-
- Send(JO);
+ else
+ {
+ ExitWithMessage($"Bad settings:\n\n{errors}");
+ }
+ }
+ catch (System.IO.FileNotFoundException e)
+ {
+ ExitWithMessage(e.Message);
}
catch (JsonException e)
{
- Show($"Settings invalid:\n\n{e.Message}");
+ ExitWithMessage($"Settings invalid:\n\n{e.Message}");
}
catch (Exception e)
{
- Show($"Error:\n\n{e}");
+ ExitWithMessage($"Error:\n\n{e}");
}
}
}
diff --git a/writer/Properties/AssemblyInfo.cs b/writer/Properties/AssemblyInfo.cs
index fc6a1ca..7f47c6d 100644
--- a/writer/Properties/AssemblyInfo.cs
+++ b/writer/Properties/AssemblyInfo.cs
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion(RawAccelVersion.value)]
-[assembly: AssemblyFileVersion(RawAccelVersion.value)]
+[assembly: AssemblyVersion(VersionHelper.VersionString)]
+[assembly: AssemblyFileVersion(VersionHelper.VersionString)] \ No newline at end of file