diff options
| author | a1xd <[email protected]> | 2020-07-31 05:12:04 -0400 |
|---|---|---|
| committer | GitHub <[email protected]> | 2020-07-31 05:12:04 -0400 |
| commit | d5c012bcd5b7321671f9591a3e58d0b2c9507467 (patch) | |
| tree | fe64110f7678607947d19c5ba0b46ea2b15d69c4 | |
| parent | Merge pull request #5 from JacobPalecki/WrapperAndGrapher (diff) | |
| parent | update grapher/wrapper for st-refactor (diff) | |
| download | rawaccel-d5c012bcd5b7321671f9591a3e58d0b2c9507467.tar.xz rawaccel-d5c012bcd5b7321671f9591a3e58d0b2c9507467.zip | |
Merge pull request #6 from a1xd/st-refactor
Refactor
| -rw-r--r-- | common/accel-base.hpp | 69 | ||||
| -rw-r--r-- | common/accel-classic.hpp | 29 | ||||
| -rw-r--r-- | common/accel-error.hpp | 11 | ||||
| -rw-r--r-- | common/accel-linear.hpp | 14 | ||||
| -rw-r--r-- | common/accel-logarithmic.hpp | 20 | ||||
| -rw-r--r-- | common/accel-natural.hpp | 31 | ||||
| -rw-r--r-- | common/accel-noaccel.hpp | 14 | ||||
| -rw-r--r-- | common/accel-power.hpp | 41 | ||||
| -rw-r--r-- | common/accel-sigmoid.hpp | 32 | ||||
| -rw-r--r-- | common/common.vcxitems | 9 | ||||
| -rw-r--r-- | common/external/nillable.h | 30 | ||||
| -rw-r--r-- | common/external/tagged-union-single.h | 202 | ||||
| -rw-r--r-- | common/rawaccel-userspace.hpp | 57 | ||||
| -rw-r--r-- | common/rawaccel.hpp | 350 | ||||
| -rw-r--r-- | common/x64-util.hpp | 10 | ||||
| -rw-r--r-- | console/console.cpp | 4 | ||||
| -rw-r--r-- | driver/driver.cpp | 26 | ||||
| -rw-r--r-- | grapher/Form1.cs | 71 | ||||
| -rw-r--r-- | wrapper/wrapper.cpp | 9 | ||||
| -rw-r--r-- | wrapper/wrapper.hpp | 38 |
20 files changed, 842 insertions, 225 deletions
diff --git a/common/accel-base.hpp b/common/accel-base.hpp new file mode 100644 index 0000000..da2c96b --- /dev/null +++ b/common/accel-base.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include "vec2.h" + +namespace rawaccel { + + // Error throwing calls std libraries which are unavailable in kernel mode. + void error(const char* s); + + using milliseconds = double; + + /// <summary> Struct to hold arguments for an acceleration function. </summary> + struct accel_args { + double offset = 0; + double accel = 0; + double limit = 2; + double exponent = 2; + double midpoint = 0; + double power_scale = 1; + vec2d weight = { 1, 1 }; + }; + + /// <summary> + /// Struct to hold common acceleration curve implementation details. + /// </summary> + struct accel_base { + + /// <summary> Coefficients applied to acceleration per axis.</summary> + vec2d weight = { 1, 1 }; + + /// <summary> Generally, the acceleration ramp rate. + double speed_coeff = 0; + + accel_base(const accel_args& args) { + verify(args); + + speed_coeff = args.accel; + weight = args.weight; + } + + /// <summary> + /// Default transformation of speed -> acceleration. + /// </summary> + inline double accelerate(double speed) const { + return speed_coeff * speed; + } + + /// <summary> + /// Default transformation of acceleration -> mouse input multipliers. + /// </summary> + inline vec2d scale(double accel_val) const { + return { + weight.x * accel_val + 1, + weight.y * accel_val + 1 + }; + } + + /// <summary> + /// Verifies arguments as valid. Errors if not. + /// </summary> + /// <param name="args">Arguments to verified.</param> + void verify(const accel_args& args) const { + if (args.accel < 0) error("accel can not be negative, use a negative weight to compensate"); + } + + accel_base() = default; + }; + +} diff --git a/common/accel-classic.hpp b/common/accel-classic.hpp new file mode 100644 index 0000000..1a2adca --- /dev/null +++ b/common/accel-classic.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include <math.h> + +#include "accel-base.hpp" + +namespace rawaccel { + + /// <summary> Struct to hold "classic" (linear raised to power) acceleration implementation. </summary> + struct accel_classic : accel_base { + double exponent; + + accel_classic(const accel_args& args) : accel_base(args) { + verify(args); + + exponent = args.exponent - 1; + } + + inline double accelerate(double speed) const { + //f(x) = (mx)^k + return pow(speed_coeff * speed, exponent); + } + + void verify(const accel_args& args) const { + if (args.exponent <= 1) error("exponent must be greater than 1"); + } + }; + +} diff --git a/common/accel-error.hpp b/common/accel-error.hpp new file mode 100644 index 0000000..fa1f999 --- /dev/null +++ b/common/accel-error.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include <stdexcept> + +namespace rawaccel { + + void error(const char* s) { + throw std::domain_error(s); + } + +} diff --git a/common/accel-linear.hpp b/common/accel-linear.hpp new file mode 100644 index 0000000..5cbb7c6 --- /dev/null +++ b/common/accel-linear.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include "accel-base.hpp" + +namespace rawaccel { + + /// <summary> Struct to hold linear acceleration implementation. </summary> + struct accel_linear : accel_base { + + using accel_base::accel_base; + + }; + +} diff --git a/common/accel-logarithmic.hpp b/common/accel-logarithmic.hpp new file mode 100644 index 0000000..928eda9 --- /dev/null +++ b/common/accel-logarithmic.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include <math.h> + +#include "accel-base.hpp" + +namespace rawaccel { + + /// <summary> Struct to hold logarithmic acceleration implementation. </summary> + struct accel_logarithmic : accel_base { + + using accel_base::accel_base; + + inline double accelerate(double speed) const { + //f(x) = log(m*x+1) + return log(speed_coeff * speed + 1); + } + }; + +} diff --git a/common/accel-natural.hpp b/common/accel-natural.hpp new file mode 100644 index 0000000..c87fda8 --- /dev/null +++ b/common/accel-natural.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include <math.h> + +#include "accel-base.hpp" + +namespace rawaccel { + + /// <summary> Struct to hold "natural" (vanishing difference) acceleration implementation. </summary> + struct accel_natural : accel_base { + double limit = 1; + double midpoint = 0; + + accel_natural(const accel_args& args) : accel_base(args) { + verify(args); + + limit = args.limit - 1; + speed_coeff /= limit; + } + + inline double accelerate(double speed) const { + // f(x) = k(1-e^(-mx)) + return limit - (limit * exp(-speed_coeff * speed)); + } + + void verify(const accel_args& args) const { + if (args.limit <= 1) error("limit must be greater than 1"); + } + }; + +} diff --git a/common/accel-noaccel.hpp b/common/accel-noaccel.hpp new file mode 100644 index 0000000..b7f730b --- /dev/null +++ b/common/accel-noaccel.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include "accel-base.hpp" + +namespace rawaccel { + + /// <summary> Struct to hold acceleration implementation which applies no acceleration. </summary> + struct accel_noaccel : accel_base { + + accel_noaccel(const accel_args&) : accel_base() {} + + }; + +} diff --git a/common/accel-power.hpp b/common/accel-power.hpp new file mode 100644 index 0000000..7f4c220 --- /dev/null +++ b/common/accel-power.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include <math.h> + +#include "accel-base.hpp" + +namespace rawaccel { + + /// <summary> Struct to hold power (non-additive) acceleration implementation. </summary> + struct accel_power : accel_base { + double exponent; + double offset; + + accel_power(const accel_args& args) { + verify(args); + + weight = args.weight; + speed_coeff = args.power_scale; + exponent = args.exponent; + offset = args.offset; + } + + inline double accelerate(double speed) const { + // f(x) = (mx)^k + return (offset > 0 && speed < 1) ? 1 : pow(speed * speed_coeff, exponent); + } + + inline vec2d scale(double accel_val) const { + return { + weight.x * accel_val, + weight.y * accel_val + }; + } + + void verify(const accel_args& args) const { + if (args.power_scale <= 0) error("scale must be positive"); + if (args.exponent <= 0) error("exponent must be greater than 0"); + } + }; + +} diff --git a/common/accel-sigmoid.hpp b/common/accel-sigmoid.hpp new file mode 100644 index 0000000..7cfa6c4 --- /dev/null +++ b/common/accel-sigmoid.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include <math.h> + +#include "accel-base.hpp" + +namespace rawaccel { + + /// <summary> Struct to hold sigmoid (s-shaped) acceleration implementation. </summary> + struct accel_sigmoid : accel_base { + double limit = 1; + double midpoint = 0; + + accel_sigmoid(const accel_args& args) : accel_base(args) { + verify(args); + + limit = args.limit - 1; + midpoint = args.midpoint; + } + + inline double accelerate(double speed) const { + //f(x) = k/(1+e^(-m(c-x))) + return limit / (exp(-speed_coeff * (speed - midpoint)) + 1); + } + + void verify(const accel_args& args) const { + if (args.limit <= 1) error("exponent must be greater than 1"); + if (args.midpoint < 0) error("midpoint must not be negative"); + } + }; + +} diff --git a/common/common.vcxitems b/common/common.vcxitems index f60f78a..d1e8db0 100644 --- a/common/common.vcxitems +++ b/common/common.vcxitems @@ -14,6 +14,15 @@ <ProjectCapability Include="SourceItemsFromImports" /> </ItemGroup> <ItemGroup> + <ClInclude Include="$(MSBuildThisFileDirectory)accel-base.hpp" /> + <ClInclude Include="$(MSBuildThisFileDirectory)accel-classic.hpp" /> + <ClInclude Include="$(MSBuildThisFileDirectory)accel-linear.hpp" /> + <ClInclude Include="$(MSBuildThisFileDirectory)accel-logarithmic.hpp" /> + <ClInclude Include="$(MSBuildThisFileDirectory)accel-natural.hpp" /> + <ClInclude Include="$(MSBuildThisFileDirectory)accel-noaccel.hpp" /> + <ClInclude Include="$(MSBuildThisFileDirectory)accel-power.hpp" /> + <ClInclude Include="$(MSBuildThisFileDirectory)accel-sigmoid.hpp" /> + <ClInclude Include="$(MSBuildThisFileDirectory)accel-error.hpp" /> <ClInclude Include="$(MSBuildThisFileDirectory)rawaccel-userspace.hpp" /> <ClInclude Include="$(MSBuildThisFileDirectory)rawaccel.hpp" /> <ClInclude Include="$(MSBuildThisFileDirectory)x64-util.hpp" /> diff --git a/common/external/nillable.h b/common/external/nillable.h new file mode 100644 index 0000000..40cf01c --- /dev/null +++ b/common/external/nillable.h @@ -0,0 +1,30 @@ +inline constexpr struct nil_t {} nil; + +// Requirements: T is default-constructible and trivially-destructible +template<typename T> +struct nillable { + bool has_value = false; + T value; + + nillable() = default; + + nillable(nil_t) : nillable() {} + nillable(const T& v) : has_value(true), value(v) {} + + nillable& operator=(nil_t) { + has_value = false; + return *this; + } + nillable& operator=(const T& v) { + value = v; + has_value = true; + return *this; + } + + const T* operator->() const { return &value; } + T* operator->() { return &value; } + + explicit operator bool() const { return has_value; } +}; + +template<typename T> nillable(T)->nillable<T>; diff --git a/common/external/tagged-union-single.h b/common/external/tagged-union-single.h new file mode 100644 index 0000000..3353325 --- /dev/null +++ b/common/external/tagged-union-single.h @@ -0,0 +1,202 @@ +#pragma once + +using size_t = decltype(alignof(char)); + +namespace type_traits { + +template< class T > struct remove_cv { typedef T type; }; +template< class T > struct remove_cv<const T> { typedef T type; }; +template< class T > struct remove_cv<volatile T> { typedef T type; }; +template< class T > struct remove_cv<const volatile T> { typedef T type; }; +template< class T > using remove_cv_t = typename remove_cv<T>::type; + +template< class T > struct remove_reference { typedef T type; }; +template< class T > struct remove_reference<T&> { typedef T type; }; +template< class T > struct remove_reference<T&&> { typedef T type; }; +template< class T > using remove_reference_t = typename remove_reference<T>::type; + +template< class T > +struct remove_cvref { + using type = remove_cv_t<remove_reference_t<T>>; +}; +template< class T > using remove_cvref_t = typename remove_cvref<T>::type; + +namespace detail { + +template <class T> struct type_identity { using type = T; }; + +template <class T> +auto try_add_lvalue_reference(int)->type_identity<T&>; +template <class T> +auto try_add_lvalue_reference(...)->type_identity<T>; + +template <class T> +auto try_add_rvalue_reference(int)->type_identity<T&&>; +template <class T> +auto try_add_rvalue_reference(...)->type_identity<T>; + +} // type_traits::detail + +template <class T> struct add_lvalue_reference : decltype(detail::try_add_lvalue_reference<T>(0)) {}; +template< class T > using add_lvalue_reference_t = typename add_lvalue_reference<T>::type; + +template <class T> struct add_rvalue_reference : decltype(detail::try_add_rvalue_reference<T>(0)) {}; +template< class T > using add_rvalue_reference_t = typename add_rvalue_reference<T>::type; + +template <typename T, typename U> inline constexpr bool is_same_v = false; +template <typename T> inline constexpr bool is_same_v<T, T> = true; +template <typename T> inline constexpr bool is_void_v = is_same_v<remove_cv_t<T>, void>; + +} // type_traits + +template<class T> type_traits::add_rvalue_reference_t<T> declval() noexcept; + +template <typename T> +inline constexpr T maxv(const T& a, const T& b) { + return (b < a) ? a : b; +} + +template <typename T> +inline constexpr T minv(const T& a, const T& b) { + return (a < b) ? a : b; +} + +template <typename T> +inline constexpr T clampv(const T& v, const T& lo, const T& hi) { + return minv(maxv(v, lo), hi); +} + +template <typename T> +inline constexpr const T& select_ref(bool pred, const T& t, const T& f) { + return pred ? t : f; +} + +template <typename First, typename... Rest> +inline constexpr size_t max_size_of = maxv(sizeof(First), max_size_of<Rest...>); + +template <typename T> +inline constexpr size_t max_size_of<T> = sizeof(T); + +template <typename First, typename... Rest> +inline constexpr size_t max_align_of = maxv(alignof(First), max_align_of<Rest...>); + +template <typename T> +inline constexpr size_t max_align_of<T> = alignof(T); + +namespace detail { + +template <typename... Ts> +struct b1_index_of_impl { + + template <typename...> + struct idx { + static constexpr size_t value = 0; + }; + + template <typename T, typename First, typename... Rest> + struct idx <T, First, Rest...> { + static constexpr size_t value = []() { + if constexpr (type_traits::is_same_v<T, First>) { + return sizeof...(Ts) - sizeof...(Rest); + } + return idx<T, Rest...>::value; + }(); + }; +}; + +} // detail + +template <typename T, typename First, typename... Rest> +inline constexpr int base1_index_of = +detail::b1_index_of_impl<First, Rest...>::template idx<T, First, Rest...>::value; + +/* +Requirements: Every type is trivially-copyable and is not an array type + +Can be initialized to an empty state as if by using +std::variant<std::monostate, First, Rest...> +*/ +template <typename First, typename... Rest> +struct tagged_union { + + // Requirements: The return type of Visitor is default-constructible (or void) + // Returns a value-initialized object when in an empty or invalid state + template<typename Visitor> + inline constexpr auto visit(Visitor vis) { + return visit_impl<Visitor, First, Rest...>(vis); + } + + template<typename Visitor> + inline constexpr auto visit(Visitor vis) const { + return visit_impl<Visitor, First, Rest...>(vis); + } + + template<typename T> + static constexpr int id = base1_index_of<T, First, Rest...>; + + int tag = 0; + + struct storage_t { + alignas(max_align_of<First, Rest...>) char bytes[max_size_of<First, Rest...>] = ""; + + template <typename T> + inline constexpr T& as() { + static_assert(id<T> != 0, "tagged_union can not hold T"); + return reinterpret_cast<T&>(bytes); + } + + template <typename T> + inline constexpr const T& as() const { + static_assert(id<T> != 0, "tagged_union can not hold T"); + return reinterpret_cast<const T&>(bytes); + } + + } storage; + + constexpr tagged_union() noexcept = default; + + template<typename T> + inline constexpr tagged_union(const T& val) noexcept { + tag = id<T>; + storage.template as<T>() = val; + } + + template<typename T> + inline constexpr tagged_union& operator=(const T& val) noexcept { + tag = id<T>; + storage.template as<T>() = val; + return *this; + } + +private: + template<typename Visitor, typename T1, typename... TRest> + inline constexpr auto visit_impl(Visitor vis) const { + if (tag == id<T1>) { + return vis(storage.template as<T1>()); + } + if constexpr (sizeof...(TRest) > 0) { + return visit_impl<Visitor, TRest...>(vis); + } + else { + using ReturnType = decltype(vis(declval<First&>())); + if constexpr (!type_traits::is_void_v<ReturnType>) return ReturnType{}; + } + } + + template<typename Visitor, typename T1, typename... TRest> + inline constexpr auto visit_impl(Visitor vis) { + if (tag == id<T1>) { + return vis(storage.template as<T1>()); + } + if constexpr (sizeof...(TRest) > 0) { + return visit_impl<Visitor, TRest...>(vis); + } + else { + using ReturnType = decltype(vis(declval<First&>())); + if constexpr (!type_traits::is_void_v<ReturnType>) return ReturnType{}; + } + } +}; + +template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; }; +template<class... Ts> overloaded(Ts...)->overloaded<Ts...>; diff --git a/common/rawaccel-userspace.hpp b/common/rawaccel-userspace.hpp index db50b35..c80262c 100644 --- a/common/rawaccel-userspace.hpp +++ b/common/rawaccel-userspace.hpp @@ -4,6 +4,7 @@ #include "external/clipp.h" +#include "accel-error.hpp" #include "rawaccel.hpp" namespace rawaccel { @@ -12,14 +13,14 @@ inline constexpr int SYSTEM_ERROR = -1; inline constexpr int PARSE_ERROR = 1; inline constexpr int INVALID_ARGUMENT = 2; -void error(const char* s) { - throw std::domain_error(s); +template<typename Accel, typename StrFirst, typename... StrRest> +clipp::parameter make_accel_cmd(modifier_args& args, StrFirst&& first_flag, StrRest&&... rest) { + return clipp::command(first_flag, rest...) + .set(args.acc_fn_args.accel_mode, accel_impl_t::id<Accel>); } -variables parse(int argc, char** argv) { - double degrees = 0; - vec2d sens = { 1, 1 }; - accel_function::args_t accel_args{}; +mouse_modifier parse(int argc, char** argv) { + modifier_args args{}; auto make_opt_vec = [](vec2d& v, auto first_flag, auto... rest) { return clipp::option(first_flag, rest...) & ( @@ -40,64 +41,64 @@ variables parse(int argc, char** argv) { }; // default options - auto opt_sens = "sensitivity (default = 1)" % make_opt_vec(sens, "sens"); + auto opt_sens = "sensitivity (default = 1)" % make_opt_vec(args.sens, "sens"); auto opt_rot = "counter-clockwise rotation (default = 0)" % ( clipp::option("rotate") & - clipp::number("degrees", degrees) + clipp::number("degrees", args.degrees) ); // mode-independent accel options auto opt_weight = "accel multiplier (default = 1)" % - make_opt_vec(accel_args.weight, "weight"); + make_opt_vec(args.acc_fn_args.acc_args.weight, "weight"); auto opt_offset = "speed (dots/ms) where accel kicks in (default = 0)" % ( - clipp::option("offset") & clipp::number("speed", accel_args.offset) + clipp::option("offset") & clipp::number("speed", args.acc_fn_args.acc_args.offset) ); auto opt_cap = "accel scale cap (default = 9)" % - make_opt_vec(accel_args.cap, "cap"); + make_opt_vec(args.acc_fn_args.cap, "cap"); auto opt_tmin = "minimum time between polls (default = 0.4)" % ( clipp::option("tmin") & - clipp::number("ms", accel_args.time_min) + clipp::number("ms", args.acc_fn_args.time_min) ); - auto accel_var = (clipp::required("accel") & clipp::number("num", accel_args.accel)) % "ramp rate"; - auto limit_var = (clipp::required("limit") & clipp::number("scale", accel_args.lim_exp)) % "limit"; + auto accel_var = (clipp::required("accel") & clipp::number("num", args.acc_fn_args.acc_args.accel)) % "ramp rate"; + auto limit_var = (clipp::required("limit") & clipp::number("scale", args.acc_fn_args.acc_args.limit)) % "limit"; + auto exp_var = (clipp::required("exponent") & clipp::number("num", args.acc_fn_args.acc_args.exponent)) % "exponent"; // modes - auto noaccel_mode = "no-accel mode" % ( - clipp::command("off", "noaccel").set(accel_args.accel_mode, mode::noaccel) - ); + auto noaccel_mode = "no-accel mode" % make_accel_cmd<accel_noaccel>(args, "off", "noaccel"); + auto lin_mode = "linear accel mode:" % ( - clipp::command("linear").set(accel_args.accel_mode, mode::linear), + make_accel_cmd<accel_linear>(args, "linear"), accel_var ); auto classic_mode = "classic accel mode:" % ( - clipp::command("classic").set(accel_args.accel_mode, mode::classic), + make_accel_cmd<accel_classic>(args, "classic"), accel_var, - (clipp::required("exponent") & clipp::number("num", accel_args.lim_exp)) % "exponent" + exp_var ); auto nat_mode = "natural accel mode:" % ( - clipp::command("natural").set(accel_args.accel_mode, mode::natural), + make_accel_cmd<accel_natural>(args, "natural"), accel_var, limit_var ); auto log_mode = "logarithmic accel mode:" % ( - clipp::command("logarithmic").set(accel_args.accel_mode, mode::logarithmic), + make_accel_cmd<accel_logarithmic>(args, "logarithmic"), accel_var ); auto sig_mode = "sigmoid accel mode:" % ( - clipp::command("sigmoid").set(accel_args.accel_mode, mode::sigmoid), + make_accel_cmd<accel_sigmoid>(args, "sigmoid"), accel_var, limit_var, - (clipp::required("midpoint") & clipp::number("speed", accel_args.midpoint)) % "midpoint" + (clipp::required("midpoint") & clipp::number("speed", args.acc_fn_args.acc_args.midpoint)) % "midpoint" ); auto pow_mode = "power accel mode:" % ( - clipp::command("power").set(accel_args.accel_mode, mode::power) >> [&] { accel_args.accel = 1; }, - (clipp::required("exponent") & clipp::number("num", accel_args.lim_exp)) % "exponent", - (clipp::option("scale") & clipp::number("num", accel_args.accel)) % "scale factor" + make_accel_cmd<accel_power>(args, "power"), + exp_var, + (clipp::option("scale") & clipp::number("num", args.acc_fn_args.acc_args.power_scale)) % "scale factor" ); auto accel_mode_exclusive = (lin_mode | classic_mode | nat_mode | log_mode | sig_mode | pow_mode); @@ -121,7 +122,7 @@ variables parse(int argc, char** argv) { std::exit(0); } - return variables(-degrees, sens, accel_args); + return mouse_modifier(args); } } // rawaccel diff --git a/common/rawaccel.hpp b/common/rawaccel.hpp index 095af76..59a0360 100644 --- a/common/rawaccel.hpp +++ b/common/rawaccel.hpp @@ -3,178 +3,216 @@ #define _USE_MATH_DEFINES #include <math.h> -#include "vec2.h" #include "x64-util.hpp" +#include "external/tagged-union-single.h" + +#include "accel-linear.hpp" +#include "accel-classic.hpp" +#include "accel-natural.hpp" +#include "accel-logarithmic.hpp" +#include "accel-sigmoid.hpp" +#include "accel-power.hpp" +#include "accel-noaccel.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 operator()(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; + }; + + /// <summary> Struct to hold clamp (min and max) details for acceleration application </summary> + struct accel_scale_clamp { + double lo = 0; + double hi = 9; + + /// <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); + } + + accel_scale_clamp(double cap) : accel_scale_clamp() { + if (cap <= 0) { + // use default, effectively uncapped accel + return; + } + + if (cap < 1) { + // assume negative accel + lo = cap; + hi = 1; + } + else hi = cap; + } -namespace rawaccel { + accel_scale_clamp() = default; + }; -enum class mode { noaccel, linear, classic, natural, logarithmic, sigmoid, power }; + /// <summary> Tagged union to hold all accel implementations and allow "polymorphism" via a visitor call. </summary> + using accel_impl_t = tagged_union<accel_linear, accel_classic, accel_natural, accel_logarithmic, accel_sigmoid, accel_power, accel_noaccel>; -struct rotator { - vec2d rot_vec = { 1, 0 }; + struct accel_fn_args { + accel_args acc_args; + int accel_mode = accel_impl_t::id<accel_noaccel>; + milliseconds time_min = 0.4; + vec2d cap = { 0, 0 }; + }; - inline vec2d operator()(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 - }; - } + /// <summary> Struct for holding acceleration application details. </summary> + struct accel_function { - rotator(double degrees) { - double rads = degrees * M_PI / 180; - rot_vec = { cos(rads), sin(rads) }; - } + /* + This value is ideally a few microseconds lower than + the user's mouse polling interval, though it should + not matter if the system is stable. + */ + /// <summary> The minimum time period for one mouse movement. </summary> + milliseconds time_min = 0.4; - rotator() = default; -}; + /// <summary> The offset past which acceleration is applied. </summary> + double speed_offset = 0; -struct accel_scale_clamp { - double lo = 0; - double hi = 9; + /// <summary> The acceleration implementation (i.e. curve) </summary> + accel_impl_t accel; - inline double operator()(double scale) const { - return clampsd(scale, lo, hi); - } + /// <summary> The object which sets a min and max for the acceleration scale. </summary> + vec2<accel_scale_clamp> clamp; - accel_scale_clamp(double cap) : accel_scale_clamp() { - if (cap <= 0) { - // use default, effectively uncapped accel - return; - } + accel_function(const accel_fn_args& args) { + if (args.time_min <= 0) error("min time must be positive"); + if (args.acc_args.offset < 0) error("offset must not be negative"); - if (cap < 1) { - // assume negative accel - lo = cap; - hi = 1; - } - else hi = cap; - } - - accel_scale_clamp() = default; -}; - -void error(const char*); - -struct accel_function { - using milliseconds = double; - - /* - This value is ideally a few microseconds lower than - the user's mouse polling interval, though it should - not matter if the system is stable. - */ - milliseconds time_min = 0.4; - - double speed_offset = 0; - - // speed midpoint in sigmoid mode - double m = 0; - - // accel ramp rate - double b = 0; - - // the limit for natural and sigmoid modes - // or the exponent for classic and power modes - double k = 1; - - vec2d weight = { 1, 1 }; - vec2<accel_scale_clamp> clamp; - - inline vec2d operator()(const vec2d& input, milliseconds time, mode accel_mode) const { - double mag = sqrtsd(input.x * input.x + input.y * input.y); - double time_clamped = clampsd(time, time_min, 100); - double speed = maxsd(mag / time_clamped - speed_offset, 0); - - double accel_val = 0; - - switch (accel_mode) { - case mode::linear: accel_val = b * speed; - break; - case mode::classic: accel_val = pow(b * speed, k); - break; - case mode::natural: accel_val = k - (k * exp(-b * speed)); - break; - case mode::logarithmic: accel_val = log(speed * b + 1); - break; - case mode::sigmoid: accel_val = k / (exp(-b * (speed - m)) + 1); - break; - case mode::power: accel_val = (speed_offset > 0 && speed < 1) ? 0 : pow(speed*b, k) - 1; - break; - default: - break; + accel.tag = args.accel_mode; + accel.visit([&](auto& impl){ impl = { args.acc_args }; }); + + time_min = args.time_min; + speed_offset = args.acc_args.offset; + clamp.x = accel_scale_clamp(args.cap.x); + clamp.y = accel_scale_clamp(args.cap.y); } - double scale_x = weight.x * accel_val + 1; - double scale_y = weight.y * accel_val + 1; + /// <summary> + /// Applies weighted acceleration to given input for given time period. + /// </summary> + /// <param name="input">2d vector of {x, y} mouse movement to be accelerated</param> + /// <param name="time">Time period over which input movement was accumulated</param> + /// <returns></returns> + inline vec2d operator()(const vec2d& input, milliseconds time) const { + double mag = sqrtsd(input.x * input.x + input.y * input.y); + double time_clamped = clampsd(time, time_min, 100); + double speed = maxsd(mag / time_clamped - speed_offset, 0); + + vec2d scale = accel.visit([=](auto&& impl) { + double accel_val = impl.accelerate(speed); + return impl.scale(accel_val); + }); + + return { + input.x * clamp.x(scale.x), + input.y * clamp.y(scale.y) + }; + } - return { - input.x * clamp.x(scale_x), - input.y * clamp.y(scale_y) - }; - } + accel_function() = default; + }; - struct args_t { - mode accel_mode = mode::noaccel; - milliseconds time_min = 0.4; - double offset = 0; - double accel = 0; - double lim_exp = 2; - double midpoint = 0; - vec2d weight = { 1, 1 }; - vec2d cap = { 0, 0 }; + struct modifier_args { + double degrees = 0; + vec2d sens = { 1, 1 }; + accel_fn_args acc_fn_args; }; - accel_function(args_t args) { - // Preconditions to guard against division by zero and - // ensure the C math functions can not return NaN or -Inf. - if (args.accel < 0) error("accel can not be negative, use a negative weight to compensate"); - if (args.time_min <= 0) error("min time must be positive"); - if (args.lim_exp <= 1) { - if (args.accel_mode == mode::classic) error("exponent must be greater than 1"); - if (args.accel_mode == mode::power) { if (args.lim_exp <=0 ) error("exponent must be greater than 0"); } - else error("limit must be greater than 1"); + /// <summary> Struct to hold variables and methods for modifying mouse input </summary> + struct mouse_modifier { + bool apply_rotate = false; + bool apply_accel = false; + rotator rotate; + accel_function accel_fn; + vec2d sensitivity = { 1, 1 }; + + mouse_modifier(const modifier_args& args) + : accel_fn(args.acc_fn_args) + { + apply_rotate = args.degrees != 0; + + if (apply_rotate) rotate = rotator(args.degrees); + else rotate = rotator(); + + apply_accel = args.acc_fn_args.accel_mode != 0 && + args.acc_fn_args.accel_mode != accel_impl_t::id<accel_noaccel>; + + if (args.sens.x == 0) sensitivity.x = 1; + else sensitivity.x = args.sens.x; + + if (args.sens.y == 0) sensitivity.y = 1; + else sensitivity.y = args.sens.y; + } + + /// <summary> + /// Applies modification without acceleration. + /// </summary> + /// <param name="input">Input to be modified.</param> + /// <returns>2d vector of modified input.</returns> + inline vec2d modify_without_accel(vec2d input) + { + if (apply_rotate) + { + input = rotate(input); + } + + input.x *= sensitivity.x; + input.y *= sensitivity.y; + + return input; } - time_min = args.time_min; - m = args.midpoint; - b = args.accel; - k = args.lim_exp - 1; - if (args.accel_mode == mode::natural) b /= k; - if (args.accel_mode == mode::power) k++; - - speed_offset = args.offset; - weight = args.weight; - clamp.x = accel_scale_clamp(args.cap.x); - clamp.y = accel_scale_clamp(args.cap.y); - } - - accel_function() = default; -}; - -struct variables { - bool apply_rotate = false; - bool apply_accel = false; - mode accel_mode = mode::noaccel; - rotator rotate; - accel_function accel_fn; - vec2d sensitivity = { 1, 1 }; - - variables(double degrees, vec2d sens, accel_function::args_t accel_args) - : accel_fn(accel_args) - { - apply_rotate = degrees != 0; - if (apply_rotate) rotate = rotator(degrees); - else rotate = rotator(); - - apply_accel = accel_args.accel_mode != mode::noaccel; - accel_mode = accel_args.accel_mode; - - if (sens.x == 0) sens.x = 1; - if (sens.y == 0) sens.y = 1; - sensitivity = sens; - } - - variables() = default; -}; - -} // rawaccel + /// <summary> + /// Applies modification, including acceleration. + /// </summary> + /// <param name="input">Input to be modified</param> + /// <param name="time">Time period for determining acceleration.</param> + /// <returns>2d vector with modified input.</returns> + inline vec2d modify_with_accel(vec2d input, milliseconds time) + { + if (apply_rotate) + { + input = rotate(input); + } + + input = accel_fn(input, time); + + input.x *= sensitivity.x; + input.y *= sensitivity.y; + + return input; + } + + mouse_modifier() = default; + }; + +} // rawaccel
\ No newline at end of file diff --git a/common/x64-util.hpp b/common/x64-util.hpp index 2fb61bb..40bc7c4 100644 --- a/common/x64-util.hpp +++ b/common/x64-util.hpp @@ -1,7 +1,13 @@ #pragma once -#include <emmintrin.h> +#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); @@ -9,6 +15,8 @@ inline double sqrtsd(double val) { return val; } +#endif + inline constexpr double minsd(double a, double b) { return (a < b) ? a : b; } diff --git a/console/console.cpp b/console/console.cpp index 6606cac..490051c 100644 --- a/console/console.cpp +++ b/console/console.cpp @@ -9,7 +9,7 @@ namespace ra = rawaccel; -void write(ra::variables vars) { +void write(ra::mouse_modifier vars) { HANDLE ra_handle = INVALID_HANDLE_VALUE; ra_handle = CreateFileW(L"\\\\.\\rawaccel", 0, 0, 0, OPEN_EXISTING, 0, 0); @@ -24,7 +24,7 @@ void write(ra::variables vars) { ra_handle, RA_WRITE, &vars, - sizeof(ra::variables), + sizeof(ra::mouse_modifier), NULL, // output buffer 0, // output buffer size &dummy, // bytes returned diff --git a/driver/driver.cpp b/driver/driver.cpp index e77ac5f..1f9cebd 100644 --- a/driver/driver.cpp +++ b/driver/driver.cpp @@ -15,7 +15,7 @@ using milliseconds = double; struct { milliseconds tick_interval = 0; // set in DriverEntry - ra::variables vars; + ra::mouse_modifier modifier; } global; VOID @@ -69,25 +69,25 @@ Arguments: static_cast<double>(it->LastY) }; - if (global.vars.apply_rotate) { - input = global.vars.rotate(input); - } - - if (global.vars.apply_accel && local_apply_accel) { + if (global.modifier.apply_accel && local_apply_accel) { auto now = KeQueryPerformanceCounter(NULL).QuadPart; auto ticks = now - devExt->counter.QuadPart; devExt->counter.QuadPart = now; milliseconds time = ticks * global.tick_interval; - if (time < global.vars.accel_fn.time_min) { + if (time < global.modifier.accel_fn.time_min) { DebugPrint(("RA time < min with %d ticks\n", ticks)); } - input = global.vars.accel_fn(input, time, global.vars.accel_mode); + input = global.modifier.modify_with_accel(input, time); + } + else + { + input = global.modifier.modify_without_accel(input); } - double result_x = input.x * global.vars.sensitivity.x + local_carry.x; - double result_y = input.y * global.vars.sensitivity.y + local_carry.y; + double result_x = input.x + local_carry.x; + double result_y = input.y + local_carry.y; LONG out_x = static_cast<LONG>(result_x); LONG out_y = static_cast<LONG>(result_y); @@ -154,7 +154,7 @@ Return Value: DebugPrint(("Ioctl received into filter control object.\n")); - if (InputBufferLength != sizeof(ra::variables)) { + if (InputBufferLength != sizeof(ra::mouse_modifier)) { DebugPrint(("Received unknown request of %u bytes\n", InputBufferLength)); // status maps to win32 error code 1784: ERROR_INVALID_USER_BUFFER WdfRequestComplete(Request, STATUS_INVALID_BUFFER_SIZE); @@ -163,7 +163,7 @@ Return Value: status = WdfRequestRetrieveInputBuffer( Request, - sizeof(ra::variables), + sizeof(ra::mouse_modifier), &input_buffer, &input_size ); @@ -175,7 +175,7 @@ Return Value: return; } - global.vars = *reinterpret_cast<ra::variables*>(input_buffer); + global.modifier = *reinterpret_cast<ra::mouse_modifier*>(input_buffer); WdfRequestComplete(Request, STATUS_SUCCESS); } diff --git a/grapher/Form1.cs b/grapher/Form1.cs index 1915b01..47ef1d9 100644 --- a/grapher/Form1.cs +++ b/grapher/Form1.cs @@ -7,22 +7,89 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; +using System.Runtime.InteropServices; namespace grapher { + public enum accel_mode + { + linear=1, classic, natural, logarithmic, sigmoid, power, noaccel + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct vec2d + { + public double x; + public double y; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct accel_args + { + public double offset; + public double accel; + public double limit; + public double exponent; + public double midpoint; + public double power_scale; + public vec2d weight; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct accel_fn_args + { + public accel_args acc_args; + public int accel_mode; + public double time_min; + public vec2d cap; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct modifier_args + { + public double degrees; + public vec2d sens; + public accel_fn_args acc_fn_args; + } + public partial class RawAcceleration : Form { public RawAcceleration() { InitializeComponent(); - var managedAccel = new ManagedAccel(6, 0, 1, 0.025, 0); + + modifier_args args; + + args.degrees = 0; + args.sens.x = 1; + args.sens.y = 1; + args.acc_fn_args.acc_args.offset = 0; + args.acc_fn_args.acc_args.accel = 0.01; + args.acc_fn_args.acc_args.limit = 2; + args.acc_fn_args.acc_args.exponent = 1; + args.acc_fn_args.acc_args.midpoint = 0; + args.acc_fn_args.acc_args.power_scale = 1; + args.acc_fn_args.acc_args.weight.x = 1; + args.acc_fn_args.acc_args.weight.y = 1; + args.acc_fn_args.accel_mode = (int)accel_mode.natural; + args.acc_fn_args.time_min = 0.4; + args.acc_fn_args.cap.x = 0; + args.acc_fn_args.cap.y = 0; + + IntPtr args_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(args)); + Marshal.StructureToPtr(args, args_ptr, false); + + var managedAccel = new ManagedAccel(args_ptr); + + Marshal.FreeHGlobal(args_ptr); + var orderedPoints = new SortedDictionary<double, double>(); for (int i = 0; i < 100; i++) { for (int j = 0; j <= i; j++) { - var output = managedAccel.Accelerate(i, j, 1, 6); + var output = managedAccel.Accelerate(i, j, 1); var inMagnitude = Magnitude(i,j); var outMagnitude = Magnitude(output.Item1, output.Item2); diff --git a/wrapper/wrapper.cpp b/wrapper/wrapper.cpp index df3f796..26d05ec 100644 --- a/wrapper/wrapper.cpp +++ b/wrapper/wrapper.cpp @@ -5,10 +5,13 @@ using namespace rawaccel; using namespace System; -Tuple<double, double>^ ManagedAccel::Accelerate(int x, int y, double time, double mode) +Tuple<double, double>^ ManagedAccel::Accelerate(int x, int y, double time) { - vec2d input_vec2d = {x, y}; - vec2d output = (*accel_instance)(input_vec2d, (accel_function::milliseconds)time, (rawaccel::mode)mode); + vec2d input_vec2d = { + (double)x, + (double)y + }; + vec2d output = (*modifier_instance).modify_with_accel(input_vec2d, (milliseconds)time); return gcnew Tuple<double, double>(output.x, output.y); }
\ No newline at end of file diff --git a/wrapper/wrapper.hpp b/wrapper/wrapper.hpp index 24bcadb..42f5865 100644 --- a/wrapper/wrapper.hpp +++ b/wrapper/wrapper.hpp @@ -1,52 +1,50 @@ #pragma once #include "..\common\rawaccel.hpp"; -#include "..\common\rawaccel-userspace.hpp"; +#include "..\common\accel-error.hpp"; #include <iostream> using namespace rawaccel; using namespace System; + +public value struct ArgsWrapper { + int a; +}; + public ref class ManagedAccel { protected: - accel_function* accel_instance; + mouse_modifier* modifier_instance; public: - ManagedAccel(accel_function* accel) - : accel_instance(accel) + ManagedAccel(mouse_modifier* accel) + : modifier_instance(accel) { } - ManagedAccel(double mode, double offset, double accel, double lim_exp, double midpoint) + ManagedAccel(System::IntPtr args) { - accel_function::args_t args{}; - args.accel = accel; - args.lim_exp = lim_exp; - args.midpoint = midpoint; - args.accel_mode = (rawaccel::mode)mode; - args.offset = offset; - - accel_instance = new accel_function(args); + modifier_instance = new mouse_modifier(*reinterpret_cast<modifier_args*>(args.ToPointer())); } virtual ~ManagedAccel() { - if (accel_instance != nullptr) + if (modifier_instance!= nullptr) { - delete accel_instance; + delete modifier_instance; } } !ManagedAccel() { - if (accel_instance != nullptr) + if (modifier_instance!= nullptr) { - delete accel_instance; + delete modifier_instance; } } - accel_function* GetInstance() + mouse_modifier* GetInstance() { - return accel_instance; + return modifier_instance; } - Tuple<double, double>^ Accelerate(int x, int y, double time, double mode); + Tuple<double, double>^ Accelerate(int x, int y, double time); };
\ No newline at end of file |