summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authora1xd <[email protected]>2021-08-28 01:19:18 -0400
committera1xd <[email protected]>2021-09-23 22:28:44 -0400
commit5b659e1cfbc4b8fbbd2f2bf41dc716929976c77d (patch)
tree4bffba32fa508494a268b6f53513fb3c7b1e3e5c /common
parentMerge pull request #107 from a1xd/1.5.0-fix (diff)
downloadrawaccel-5b659e1cfbc4b8fbbd2f2bf41dc716929976c77d.tar.xz
rawaccel-5b659e1cfbc4b8fbbd2f2bf41dc716929976c77d.zip
add per-device configuration
adds input and [in, out] cap for classic mode adds input cap for power mode change wrapper/input, now gets useful device names change (now dev specific) dpi to adjust sensitivity change y sensitivity to y/x ratio remove spaced LUTs grapher and convert do not build
Diffstat (limited to 'common')
-rw-r--r--common/accel-classic.hpp133
-rw-r--r--common/accel-invoke.hpp52
-rw-r--r--common/accel-jump.hpp18
-rw-r--r--common/accel-lookup.hpp139
-rw-r--r--common/accel-motivity.hpp79
-rw-r--r--common/accel-natural.hpp16
-rw-r--r--common/accel-noaccel.hpp2
-rw-r--r--common/accel-power.hpp66
-rw-r--r--common/accel-union.hpp123
-rw-r--r--common/common.vcxitems1
-rw-r--r--common/rawaccel-base.hpp84
-rw-r--r--common/rawaccel-validate.hpp150
-rw-r--r--common/rawaccel.hpp221
-rw-r--r--common/utility.hpp6
14 files changed, 555 insertions, 535 deletions
diff --git a/common/accel-classic.hpp b/common/accel-classic.hpp
index 4385897..ce343e7 100644
--- a/common/accel-classic.hpp
+++ b/common/accel-classic.hpp
@@ -10,77 +10,135 @@ namespace rawaccel {
/// <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, double accel_raised, const accel_args& args) const
+ {
+ return accel_raised * pow(x - args.offset, args.exponent_classic) / x;
+ }
- double base_fn(double x) const
+ static double base_accel(double x, double y, double power, double offset)
{
- return accel_raised * pow(x - offset, power) / x;
+ return pow(x * y * pow(x - offset, -power), 1 / (power + 1));
}
};
- struct classic_legacy : classic_base {
- double sens_cap = DBL_MAX;
+ template <bool Gain> struct classic;
+
+ template<>
+ struct classic<LEGACY> : classic_base {
+ double accel_raised;
+ double cap = DBL_MAX;
double sign = 1;
- classic_legacy(const accel_args& args) :
- classic_base(args)
+ classic(const accel_args& args)
{
- if (args.cap > 0) {
- sens_cap = args.cap - 1;
+ switch (args.cap_mode) {
+ case classic_cap_mode::io:
+ cap = args.cap.y - 1;
- if (sens_cap < 0) {
- sens_cap = -sens_cap;
+ if (cap < 0) {
+ cap = -cap;
sign = -sign;
}
+
+ {
+ double a = base_accel(args.cap.x, cap, args.exponent_classic, args.offset);
+ accel_raised = pow(a, args.exponent_classic - 1);
+ }
+ break;
+ case classic_cap_mode::in:
+ accel_raised = pow(args.acceleration, args.exponent_classic - 1);
+ if (args.cap.x > 0) {
+ cap = base_fn(args.cap.x, accel_raised, args);
+ }
+ break;
+ case classic_cap_mode::out:
+ default:
+ accel_raised = pow(args.acceleration, args.exponent_classic - 1);
+
+ if (args.cap.y > 0) {
+ cap = args.cap.y - 1;
+
+ if (cap < 0) {
+ cap = -cap;
+ sign = -sign;
+ }
+ }
+
+ break;
}
}
- double operator()(double x) const
+ double operator()(double x, const accel_args& args) const
{
- if (x <= offset) return 1;
- return sign * minsd(base_fn(x), sens_cap) + 1;
- }
+ if (x <= args.offset) return 1;
+ return sign * minsd(base_fn(x, accel_raised, args), cap) + 1;
+ }
+
};
- struct classic : classic_base {
- vec2d gain_cap = { DBL_MAX, DBL_MAX };
+ template<>
+ struct classic<GAIN> : classic_base {
+ double accel_raised;
+ vec2d cap = { DBL_MAX, DBL_MAX };
double constant = 0;
double sign = 1;
- classic(const accel_args& args) :
- classic_base(args)
+ classic(const accel_args& args)
{
- if (args.cap > 0) {
- gain_cap.y = args.cap - 1;
+ switch (args.cap_mode) {
+ case classic_cap_mode::io:
+ cap.x = args.cap.x;
+ cap.y = args.cap.y - 1;
- if (gain_cap.y < 0) {
- gain_cap.y = -gain_cap.y;
+ if (cap.y < 0) {
+ cap.y = -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 a = gain_accel(cap.x, cap.y, args.exponent_classic, args.offset);
+ accel_raised = pow(a, args.exponent_classic - 1);
+ }
+ constant = (base_fn(cap.x, accel_raised, args) - cap.y) * cap.x;
+ break;
+ case classic_cap_mode::in:
+ if (args.cap.x > 0) {
+ cap.x = args.cap.x;
+ cap.y = gain(cap.x, args.acceleration, args.exponent_classic, args.offset);
+ constant = (base_fn(cap.x, accel_raised, args) - cap.y) * cap.x;
+ }
+ accel_raised = pow(args.acceleration, args.exponent_classic - 1);
+ break;
+ case classic_cap_mode::out:
+ default:
+ accel_raised = pow(args.acceleration, args.exponent_classic - 1);
+
+ if (args.cap.y > 0) {
+ cap.y = args.cap.y - 1;
+
+ if (cap.y < 0) {
+ cap.y = -cap.y;
+ sign = -sign;
+ }
+
+ cap.x = gain_inverse(cap.y, args.acceleration, args.exponent_classic, args.offset);
+ constant = (base_fn(cap.x, accel_raised, args) - cap.y) * cap.x;
+ }
+ break;
}
}
- double operator()(double x) const
+ double operator()(double x, const accel_args& args) const
{
double output;
- if (x <= offset) return 1;
+ if (x <= args.offset) return 1;
- if (x < gain_cap.x) {
- output = base_fn(x);
+ if (x < cap.x) {
+ output = base_fn(x, accel_raised, args);
}
else {
- output = constant / x + gain_cap.y;
+ output = constant / x + cap.y;
}
return sign * output + 1;
@@ -100,6 +158,7 @@ namespace rawaccel {
{
return -pow(y / power, 1 / (power - 1)) / (offset - x);
}
+
};
}
diff --git a/common/accel-invoke.hpp b/common/accel-invoke.hpp
deleted file mode 100644
index f2a95dc..0000000
--- a/common/accel-invoke.hpp
+++ /dev/null
@@ -1,52 +0,0 @@
-#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
index 95fa461..c036810 100644
--- a/common/accel-jump.hpp
+++ b/common/accel-jump.hpp
@@ -2,6 +2,8 @@
#include "rawaccel-base.hpp"
+#include <math.h>
+
namespace rawaccel {
struct jump_base {
@@ -12,7 +14,7 @@ namespace rawaccel {
// requirements: args.smooth in range [0, 1]
jump_base(const accel_args& args) :
- step({ args.offset, args.cap - 1 })
+ step({ args.cap.x, args.cap.y - 1 })
{
double rate_inverse = args.smooth * step.x;
@@ -43,12 +45,16 @@ namespace rawaccel {
{
return step.y * (x + log(1 + decay(x)) / smooth_rate);
}
+
};
- struct jump_legacy : jump_base {
+ template <bool Gain> struct jump;
+
+ template<>
+ struct jump<LEGACY> : jump_base {
using jump_base::jump_base;
- double operator()(double x) const
+ double operator()(double x, const accel_args&) const
{
if (is_smooth()) return smooth(x) + 1;
else if (x < step.x) return 1;
@@ -56,14 +62,15 @@ namespace rawaccel {
}
};
- struct jump : jump_base {
+ template<>
+ struct jump<GAIN> : jump_base {
double C;
jump(const accel_args& args) :
jump_base(args),
C(-smooth_antideriv(0)) {}
- double operator()(double x) const
+ double operator()(double x, const accel_args&) const
{
if (x <= 0) return 1;
@@ -72,6 +79,7 @@ namespace rawaccel {
if (x < step.x) return 1;
else return 1 + step.y * (x - step.x) / x;
}
+
};
}
diff --git a/common/accel-lookup.hpp b/common/accel-lookup.hpp
index 99f39e9..b7e8b68 100644
--- a/common/accel-lookup.hpp
+++ b/common/accel-lookup.hpp
@@ -7,27 +7,6 @@
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 {
@@ -55,103 +34,7 @@ namespace rawaccel {
}
};
- 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 {
+ struct si_pair {
float slope = 0;
float intercept = 0;
};
@@ -161,10 +44,11 @@ namespace rawaccel {
si_pair slope_intercept = {};
};
- struct arbitrary_lut {
- enum { capacity = ARB_LUT_CAPACITY };
+ struct lookup {
+ enum { capacity = LUT_POINTS_CAPACITY };
fp_rep_range range;
+ bool velocity_points;
arbitrary_lut_point data[capacity] = {};
int log_lookup[capacity] = {};
double first_point_speed;
@@ -173,9 +57,8 @@ namespace rawaccel {
int last_log_lookup_index;
double last_log_lookup_speed;
double first_log_lookup_speed;
- bool velocity_points;
- double operator()(double speed) const
+ double operator()(double speed, const accel_args&) const
{
int index = 0;
int last_arb_index = last_arbitrary_index;
@@ -247,8 +130,11 @@ namespace rawaccel {
}
}
- void fill(const vec2<float>* points, int length)
+ void fill(const float* raw_data, int raw_length)
{
+ auto* points = reinterpret_cast<const vec2<float>*>(raw_data);
+ int length = raw_length / 2;
+
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
@@ -297,10 +183,11 @@ namespace rawaccel {
}
}
- arbitrary_lut(const accel_args& args)
+ lookup(const accel_args& args)
{
- velocity_points = args.arb_args.velocity;
- fill(args.arb_args.data, args.arb_args.length);
+ velocity_points = args.gain;
+ fill(args.data, args.length);
}
};
+
}
diff --git a/common/accel-motivity.hpp b/common/accel-motivity.hpp
index 0efe7ea..0cd60f8 100644
--- a/common/accel-motivity.hpp
+++ b/common/accel-motivity.hpp
@@ -6,39 +6,50 @@
namespace rawaccel {
- struct sigmoid {
+ template <bool Gain> struct loglog_sigmoid;
+
+ template <>
+ struct loglog_sigmoid<LEGACY> {
double accel;
double motivity;
double midpoint;
double constant;
- sigmoid(const accel_args& args) :
+ loglog_sigmoid(const accel_args& args) :
accel(exp(args.growth_rate)),
motivity(2 * log(args.motivity)),
midpoint(log(args.midpoint)),
constant(-motivity / 2) {}
- double operator()(double x) const
+ double operator()(double x, const accel_args&) const
{
double denom = exp(accel * (midpoint - log(x))) + 1;
return exp(motivity / denom + constant);
}
+
};
- /// <summary> Struct to hold sigmoid (s-shaped) gain implementation. </summary>
- struct motivity : binlog_lut {
+ template <>
+ struct loglog_sigmoid<GAIN> {
+ enum { capacity = LUT_RAW_DATA_CAPACITY };
- using binlog_lut::operator();
+ bool velocity;
+ fp_rep_range range;
+ double x_start;
- motivity(const accel_args& args) :
- binlog_lut(args)
+ loglog_sigmoid(const accel_args& args)
{
+ init({ 0, 8, 8 }, args.gain);
+
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;
+ auto sig = loglog_sigmoid<LEGACY>(args);
+ auto sigmoid_sum = [&](double b) {
+ int partitions = 2;
+
+ double interval = (b - a) / partitions;
+ for (int i = 1; i <= partitions; i++) {
+ sum += sig(a + i * interval, args) * interval;
}
a = b;
return sum;
@@ -46,9 +57,49 @@ namespace rawaccel {
fill([&](double x) {
double y = sigmoid_sum(x);
- if (!this->transfer) y /= x;
+ if (!velocity) y /= x;
return y;
- });
+ }, args, range);
+ }
+
+ double operator()(double x, const accel_args& args) const
+ {
+ auto* data = args.data;
+
+ 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 (velocity) y /= x;
+ return y;
+ }
+ }
+
+ double y = data[0];
+ if (velocity) y /= x_start;
+ return y;
+ }
+
+ void init(const fp_rep_range& r, bool vel)
+ {
+ velocity = vel;
+ range = r;
+ x_start = scalbn(1, range.start);
+ }
+
+ template <typename Func>
+ static void fill(Func fn, const accel_args& args, const fp_rep_range& range)
+ {
+ range.for_each([&, fn, i = 0](double x) mutable {
+ args.data[i++] = static_cast<float>(fn(x));
+ });
}
};
diff --git a/common/accel-natural.hpp b/common/accel-natural.hpp
index 9f76d1a..c5e1c32 100644
--- a/common/accel-natural.hpp
+++ b/common/accel-natural.hpp
@@ -18,11 +18,15 @@ namespace rawaccel {
{
accel = args.decay_rate / fabs(limit);
}
+
};
- struct natural_legacy : natural_base {
+ template<bool Gain> struct natural;
+
+ template<>
+ struct natural<LEGACY> : natural_base {
- double operator()(double x) const
+ double operator()(double x, const accel_args&) const
{
if (x <= offset) return 1;
@@ -34,10 +38,11 @@ namespace rawaccel {
using natural_base::natural_base;
};
- struct natural : natural_base {
+ template<>
+ struct natural<GAIN> : natural_base {
double constant;
- double operator()(double x) const
+ double operator()(double x, const accel_args&) const
{
if (x <= offset) return 1;
@@ -50,6 +55,7 @@ namespace rawaccel {
natural(const accel_args& args) :
natural_base(args),
constant(-limit / accel) {}
- };
+ natural() = default;
+ };
}
diff --git a/common/accel-noaccel.hpp b/common/accel-noaccel.hpp
index 8d1e758..b307d99 100644
--- a/common/accel-noaccel.hpp
+++ b/common/accel-noaccel.hpp
@@ -10,7 +10,7 @@ namespace rawaccel {
accel_noaccel(const accel_args&) {}
accel_noaccel() = default;
- double operator()(double) const { return 1; }
+ double operator()(double, const accel_args&) const { return 1; }
};
}
diff --git a/common/accel-power.hpp b/common/accel-power.hpp
index c8faabb..f727369 100644
--- a/common/accel-power.hpp
+++ b/common/accel-power.hpp
@@ -3,25 +3,67 @@
#include "rawaccel-base.hpp"
#include <math.h>
+#include <float.h>
namespace rawaccel {
- /// <summary> Struct to hold power (non-additive) acceleration implementation. </summary>
- struct power {
- double pre_scale;
- double exponent;
- double post_scale;
+ struct power_base {
+ static double base_fn(double x, const accel_args& args)
+ {
+ // f(x) = w(mx)^k
+ return args.weight * pow(args.scale * x, args.exponent_power);
+ }
+ };
- power(const accel_args& args) :
- pre_scale(args.scale),
- exponent(args.exponent),
- post_scale(args.weight) {}
+ template <bool Gain> struct power;
- double operator()(double speed) const
+ template <>
+ struct power<LEGACY> : power_base {
+ vec2d cap = { DBL_MAX, DBL_MAX };
+
+ power(const accel_args& args)
{
- // f(x) = (mx)^k
- return post_scale * pow(speed * pre_scale, exponent);
+ if (args.cap.x > 0) {
+ cap.x = args.cap.x;
+ cap.y = base_fn(cap.x, args);
+ }
}
+
+ double operator()(double speed, const accel_args& args) const
+ {
+ if (speed < cap.x) {
+ return base_fn(speed, args);
+ }
+ return cap.y;
+ }
+
+ };
+
+ template <>
+ struct power<GAIN> : power_base {
+ vec2d cap = { DBL_MAX, DBL_MAX };
+ double constant = 0;
+
+ power(const accel_args& args)
+ {
+ if (args.cap.x > 0) {
+ cap.x = args.cap.x;
+ double output = base_fn(cap.x, args);
+ cap.y = output * (args.exponent_power + 1);
+ constant = -args.exponent_power * output * args.cap.x;
+ }
+ }
+
+ double operator()(double speed, const accel_args& args) const
+ {
+ if (speed < cap.x) {
+ return base_fn(speed, args);
+ }
+ else {
+ return cap.y + constant / speed;
+ }
+ }
+
};
}
diff --git a/common/accel-union.hpp b/common/accel-union.hpp
index 8495a62..19fd9fe 100644
--- a/common/accel-union.hpp
+++ b/common/accel-union.hpp
@@ -2,101 +2,56 @@
#include "accel-classic.hpp"
#include "accel-jump.hpp"
-#include "accel-natural.hpp"
-#include "accel-power.hpp"
+#include "accel-lookup.hpp"
#include "accel-motivity.hpp"
+#include "accel-natural.hpp"
#include "accel-noaccel.hpp"
+#include "accel-power.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);
+ union accel_union {
+ accel_noaccel noaccel;
+ lookup lut;
+ classic<GAIN> classic_g;
+ classic<LEGACY> classic_l;
+ jump<GAIN> jump_g;
+ jump<LEGACY> jump_l;
+ natural<GAIN> natural_g;
+ natural<LEGACY> natural_l;
+ power<GAIN> power_g;
+ power<LEGACY> power_l;
+ loglog_sigmoid<GAIN> loglog_sigmoid_g;
+ loglog_sigmoid<LEGACY> loglog_sigmoid_l;
+
+ void init(const accel_args& args)
+ {
+ visit([&](auto& impl) {
+ impl = { args };
+ }, args);
}
- }
- 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);
+ template <typename Visitor>
+ auto visit(Visitor vis, const accel_args& args)
+ {
+ switch (args.mode) {
+ case accel_mode::classic: return visit_helper<classic>(vis, args.gain);
+ case accel_mode::jump: return visit_helper<jump>(vis, args.gain);
+ case accel_mode::natural: return visit_helper<natural>(vis, args.gain);
+ case accel_mode::motivity: return visit_helper<loglog_sigmoid>(vis, args.gain);
+ case accel_mode::power: return visit_helper<power>(vis, args.gain);
+ case accel_mode::lookup: return vis(lut);
+ default: return vis(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)
+ private:
+ template <template <bool> class AccelTemplate, typename Visitor>
+ auto visit_helper(Visitor vis, bool gain)
{
- visit_accel([&](auto& impl) {
- impl = { args };
- }, make_mode(args), *this);
+ if (gain) return vis(reinterpret_cast<AccelTemplate<GAIN>&>(*this));
+ else return vis(reinterpret_cast<AccelTemplate<LEGACY>&>(*this));
}
-
- accel_union() = default;
};
}
diff --git a/common/common.vcxitems b/common/common.vcxitems
index 296fbfd..85af72e 100644
--- a/common/common.vcxitems
+++ b/common/common.vcxitems
@@ -15,7 +15,6 @@
</ItemGroup>
<ItemGroup>
<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" />
diff --git a/common/rawaccel-base.hpp b/common/rawaccel-base.hpp
index c1b2db3..ce6c103 100644
--- a/common/rawaccel-base.hpp
+++ b/common/rawaccel-base.hpp
@@ -13,97 +13,81 @@ namespace rawaccel {
inline constexpr milliseconds WRITE_DELAY = 1000;
+ inline constexpr size_t POOL_SIZE = 1024 * 512;
+
inline constexpr size_t MAX_DEV_ID_LEN = 200;
+ inline constexpr size_t MAX_NAME_LEN = 256;
- inline constexpr size_t SPACED_LUT_CAPACITY = 1025;
- inline constexpr size_t ARB_LUT_CAPACITY = SPACED_LUT_CAPACITY / 4;
+ inline constexpr size_t LUT_RAW_DATA_CAPACITY = 258;
+ inline constexpr size_t LUT_POINTS_CAPACITY = LUT_RAW_DATA_CAPACITY / 2;
inline constexpr double MAX_NORM = 16;
inline constexpr double PI = 3.14159265358979323846;
+ inline constexpr bool LEGACY = 0;
+ inline constexpr bool GAIN = 1;
+
enum class accel_mode {
classic,
jump,
natural,
motivity,
power,
- arb_lookup,
+ 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] = {};
+ enum class classic_cap_mode {
+ io, in, out
};
struct accel_args {
accel_mode mode = accel_mode::noaccel;
- bool legacy = false;
+ bool gain = 1;
double offset = 0;
- double cap = 1.5;
- double accel_classic = 0.005;
+ double acceleration = 0.005;
double decay_rate = 0.1;
double growth_rate = 1;
double motivity = 1.5;
- double power = 2;
+ double exponent_classic = 2;
double scale = 1;
double weight = 1;
- double exponent = 0.05;
+ double exponent_power = 0.05;
double limit = 1.5;
double midpoint = 5;
double smooth = 0.5;
+ vec2d cap = { 15, 1.5 };
+ classic_cap_mode cap_mode = classic_cap_mode::out;
- spaced_lut_args spaced_args;
- table_args arb_args;
+ int length = 0;
+ mutable float data[LUT_RAW_DATA_CAPACITY] = {};
};
- struct domain_args {
- vec2d domain_weights = { 1, 1 };
+
+ struct profile {
+ wchar_t name[MAX_NAME_LEN] = L"default";
+
+ bool whole = true;
double lp_norm = 2;
- };
+ vec2d domain_weights = { 1, 1 };
+ vec2d range_weights = { 1, 1 };
+
+ double sensitivity = 1;
+ double yx_sens_ratio = 1;
+
+ accel_args accel_x;
+ accel_args accel_y;
- 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;
+ double degrees_rotation = 0;
- bool ignore = false;
- wchar_t device_id[MAX_DEV_ID_LEN] = {};
+ double degrees_snap = 0;
};
- template <typename AccelFunc>
- inline double apply_weighted(AccelFunc&& f, double x, double w)
- {
- return 1 + (f(x) - 1) * w;
- }
}
diff --git a/common/rawaccel-validate.hpp b/common/rawaccel-validate.hpp
index a03f56a..e901a8a 100644
--- a/common/rawaccel-validate.hpp
+++ b/common/rawaccel-validate.hpp
@@ -5,7 +5,7 @@
namespace rawaccel {
- struct valid_ret_t {
+ struct valid_profile_ret_t {
int last_x = 0;
int last_y = 0;
int count = 0;
@@ -16,10 +16,19 @@ namespace rawaccel {
}
};
+ struct valid_device_ret_t {
+ int count = 0;
+
+ explicit operator bool() const
+ {
+ return count == 0;
+ }
+ };
+
template <typename MsgHandler = noop>
- valid_ret_t valid(const settings& args, MsgHandler fn = {})
+ valid_profile_ret_t valid(const profile& args, MsgHandler fn = {})
{
- valid_ret_t ret;
+ valid_profile_ret_t ret;
auto error = [&](auto msg) {
++ret.count;
@@ -27,56 +36,18 @@ namespace rawaccel {
};
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);
+ static_assert(LUT_POINTS_CAPACITY == 129, "update error msg");
- 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");
+ if (args.mode == accel_mode::lookup) {
+ if (args.length < 4) {
+ error("lookup mode requires at least 2 points");
}
- else if (((lut_args.stop - lut_args.start) * lut_args.num_elements) >= 1025) {
- error("binlog mode requires (num * (stop - start)) < 1025");
+ else if (args.length > ra::LUT_RAW_DATA_CAPACITY) {
+ error("too many data points (max=129)");
}
}
-
- if (args.mode == accel_mode::arb_lookup) {
- if (args.arb_args.length < 2) {
- error("lookup mode requires at least 2 points");
- }
+ else if (args.length > ra::LUT_RAW_DATA_CAPACITY) {
+ error("data size > max");
}
if (args.offset < 0) {
@@ -86,16 +57,28 @@ namespace rawaccel {
error("offset can not be 0");
}
- if (args.cap < 0) {
- error("cap"" must not be negative");
+ bool jump_or_io_cap =
+ (args.mode == accel_mode::jump ||
+ (args.mode == accel_mode::classic &&
+ args.cap_mode == classic_cap_mode::io));
+
+ if (args.cap.x < 0) {
+ error("cap (input) can not be negative");
+ }
+ else if (args.cap.x == 0 && jump_or_io_cap) {
+ error("cap (input) can not be 0");
+ }
+
+ if (args.cap.y < 0) {
+ error("cap (output) can not be negative");
}
- else if (args.mode == accel_mode::jump && args.cap == 0) {
- error("cap can not be 0");
+ else if (args.cap.y == 0 && jump_or_io_cap) {
+ error("cap (output) can not be 0");
}
if (args.growth_rate <= 0 ||
args.decay_rate <= 0 ||
- args.accel_classic <= 0) {
+ args.acceleration <= 0) {
error("acceleration"" must be positive");
}
@@ -103,8 +86,8 @@ namespace rawaccel {
error("motivity must be greater than 1");
}
- if (args.power <= 1) {
- error("power must be greater than 1");
+ if (args.exponent_classic <= 1) {
+ error("exponent must be greater than 1");
}
if (args.scale <= 0) {
@@ -115,7 +98,7 @@ namespace rawaccel {
error("weight"" must be positive");
}
- if (args.exponent <= 0) {
+ if (args.exponent_power <= 0) {
error("exponent"" must be positive");
}
@@ -133,16 +116,16 @@ namespace rawaccel {
};
- check_accel(args.argsv.x);
+ check_accel(args.accel_x);
- if (!args.combine_mags) {
+ if (!args.whole) {
ret.last_x = ret.count;
- check_accel(args.argsv.y);
+ check_accel(args.accel_y);
ret.last_y = ret.count;
}
- if (args.dpi <= 0) {
- error("dpi"" must be positive");
+ if (args.name[0] == L'\0') {
+ error("profile name can not be empty");
}
if (args.speed_max < 0) {
@@ -156,36 +139,61 @@ namespace rawaccel {
error("snap angle must be between 0 and 45 degrees");
}
- if (args.sens.x == 0 || args.sens.y == 0) {
+ if (args.sensitivity == 0) {
error("sens multiplier is 0");
}
+
+ if (args.yx_sens_ratio == 0) {
+ error("Y/X sens ratio is 0");
+ }
- if (args.dom_args.domain_weights.x <= 0 ||
- args.dom_args.domain_weights.y <= 0) {
+ if (args.domain_weights.x <= 0 ||
+ 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");
+ error("negative directional multipliers must be positive");
}
- if (args.dom_args.lp_norm < 2) {
- error("Lp norm is less than 2 (default=2)");
+ if (args.lp_norm <= 0) {
+ error("Lp norm must be positive (default=2)");
}
if (args.range_weights.x <= 0 || args.range_weights.y <= 0) {
error("range weights"" must be positive");
}
- if (args.time_min <= 0) {
+ return ret;
+ }
+
+ template <typename MsgHandler = noop>
+ valid_device_ret_t valid(const device_settings& args, MsgHandler fn = {})
+ {
+ valid_device_ret_t ret;
+
+ auto error = [&](auto msg) {
+ ++ret.count;
+ fn(msg);
+ };
+
+
+ if (args.config.dpi < 0) {
+ error("dpi"" can not be negative");
+ }
+
+ if (args.config.polling_rate < 0) {
+ error("polling rate"" can not be negative");
+ }
+
+ if (args.config.clamp.min <= 0) {
error("minimum time"" must be positive");
}
- if (args.time_max < args.time_min) {
+ if (args.config.clamp.max < args.config.clamp.min) {
error("max time is less than min time");
}
return ret;
}
-
}
diff --git a/common/rawaccel.hpp b/common/rawaccel.hpp
index 4e8b46c..4f98c8d 100644
--- a/common/rawaccel.hpp
+++ b/common/rawaccel.hpp
@@ -1,6 +1,6 @@
#pragma once
-#include "accel-invoke.hpp"
+#include "accel-union.hpp"
namespace rawaccel {
@@ -28,45 +28,75 @@ namespace rawaccel {
return pow(pow(v.x, p) + pow(v.y, p), 1 / p);
}
- class mouse_modifier {
- public:
- enum accel_distance_mode : unsigned char {
- separate,
- max,
- Lp,
- euclidean,
+ struct time_clamp {
+ milliseconds min = DEFAULT_TIME_MIN;
+ milliseconds max = DEFAULT_TIME_MAX;
+ };
+
+ struct device_config {
+ bool disable = false;
+ bool set_extra_info = false;
+ int dpi = 0;
+ int polling_rate = 0;
+ time_clamp clamp;
+ };
+
+ struct device_settings {
+ wchar_t name[MAX_NAME_LEN] = {};
+ wchar_t profile[MAX_NAME_LEN] = {};
+ wchar_t id[MAX_DEV_ID_LEN] = {};
+ device_config config;
+ };
+
+ struct driver_settings {
+ profile prof;
+
+ struct data_t {
+ vec2d rot_direction;
+ accel_union accel_x;
+ accel_union accel_y;
+ } data = {};
+ };
+
+ inline void init_data(driver_settings& settings)
+ {
+ auto set_accel = [](accel_union& u, const accel_args& args) {
+ u.visit([&](auto& impl) {
+ impl = { args };
+ }, args);
};
- 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;
-
- 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;
+ set_accel(settings.data.accel_x, settings.prof.accel_x);
+ set_accel(settings.data.accel_y, settings.prof.accel_y);
+
+ settings.data.rot_direction = direction(settings.prof.degrees_rotation);
+ }
+
+ inline constexpr unsigned DRIVER_CAPACITY = POOL_SIZE / sizeof(driver_settings);
+ inline constexpr unsigned DEVICE_CAPACITY = POOL_SIZE / sizeof(device_settings);
+ struct io_t {
+ device_config default_dev_cfg;
+ unsigned driver_data_size;
+ unsigned device_data_size;
+ driver_settings driver_data[DRIVER_CAPACITY];
+ device_settings device_data[DEVICE_CAPACITY];
+ };
+
+ class modifier {
+ public:
#ifdef _KERNEL_MODE
__forceinline
#endif
- void modify(vec2d& in, const vec2<accel_invoker>& inv, milliseconds time = 1) const
+ void modify(vec2d& in, const driver_settings& settings, double dpi_factor, milliseconds time) const
{
- double ips_factor = dpi_norm_factor / time;
+ auto& args = settings.prof;
+ auto& data = settings.data;
+
double reference_angle = 0;
+ double ips_factor = dpi_factor / time;
- if (apply_rotate) in = rotate(in, rot_vec);
+ if (apply_rotate) in = rotate(in, data.rot_direction);
if (compute_ref_angle && in.y != 0) {
if (in.x == 0) {
@@ -76,6 +106,8 @@ namespace rawaccel {
reference_angle = atan(fabs(in.y / in.x));
if (apply_snap) {
+ double snap = args.degrees_snap * PI / 180;
+
if (reference_angle > PI / 2 - snap) {
reference_angle = PI / 2;
in = { 0, _copysign(magnitude(in), in.y) };
@@ -88,92 +120,127 @@ namespace rawaccel {
}
}
- if (cap_speed) {
+ if (clamp_speed) {
double speed = magnitude(in) * ips_factor;
- double ratio = clampsd(speed, speed_min, speed_max) / speed;
+ double ratio = clampsd(speed, args.speed_min, args.speed_max) / speed;
in.x *= ratio;
in.y *= ratio;
}
vec2d abs_weighted_vel = {
- fabs(in.x * ips_factor * domain_weights.x),
- fabs(in.y * ips_factor * domain_weights.y)
+ fabs(in.x * ips_factor * args.domain_weights.x),
+ fabs(in.y * ips_factor * args.domain_weights.y)
};
- 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);
+ if (distance_mode == separate) {
+ in.x *= (*cb_x)(data.accel_x, args.accel_x, abs_weighted_vel.x, args.range_weights.x);
+ in.y *= (*cb_y)(data.accel_y, args.accel_y, abs_weighted_vel.y, args.range_weights.y);
}
- else {
+ else {
double speed;
- if (dist_mode == max) {
+ if (distance_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 if (distance_mode == Lp) {
+ speed = lp_distance(abs_weighted_vel, args.lp_norm);
}
else {
speed = magnitude(abs_weighted_vel);
}
- double weight = range_weights.x;
+ double weight = args.range_weights.x;
if (apply_directional_weight) {
- double diff = range_weights.y - range_weights.x;
+ double diff = args.range_weights.y - args.range_weights.x;
weight += 2 / PI * reference_angle * diff;
}
- double scale = inv.x.invoke(accel.x, speed, weight);
+ double scale = (*cb_x)(data.accel_x, args.accel_x, speed, weight);
in.x *= scale;
in.y *= scale;
}
+ double dpi_adjusted_sens = args.sensitivity * dpi_factor;
+ in.x *= dpi_adjusted_sens;
+ in.y *= dpi_adjusted_sens * args.yx_sens_ratio;
+
if (apply_dir_mul_x && in.x < 0) {
- in.x *= directional_multipliers.x;
+ in.x *= args.dir_multipliers.x;
}
if (apply_dir_mul_y && in.y < 0) {
- in.y *= directional_multipliers.y;
+ in.y *= args.dir_multipliers.y;
}
-
- in.x *= sensitivity.x;
- in.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 } })
+ modifier(driver_settings& settings)
{
- 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;
+ auto& args = settings.prof;
+
+ clamp_speed = args.speed_max > 0 && args.speed_min <= args.speed_max;
+ apply_rotate = args.degrees_rotation != 0;
+ apply_snap = args.degrees_snap != 0;
+ apply_directional_weight = args.range_weights.x != args.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;
+ apply_dir_mul_x = args.dir_multipliers.x != 1;
+ apply_dir_mul_y = args.dir_multipliers.y != 1;
- if (!args.combine_mags) dist_mode = separate;
- else if (p >= MAX_NORM || p <= 0) dist_mode = max;
- else if (p != 2) dist_mode = Lp;
- else dist_mode = euclidean;
+ if (!args.whole) {
+ distance_mode = distance_mode::separate;
+ }
+ else if (args.lp_norm >= MAX_NORM || args.lp_norm <= 0) {
+ distance_mode = distance_mode::max;
+ }
+ else if (args.lp_norm != 2) {
+ distance_mode = distance_mode::Lp;
+ }
+ else {
+ distance_mode = distance_mode::euclidean;
+ }
+
+ set_callback(cb_x, settings.data.accel_x, args.accel_x);
+ set_callback(cb_y, settings.data.accel_y, args.accel_y);
}
- mouse_modifier() = default;
- };
+ modifier() = default;
- struct io_t {
- settings args;
- mouse_modifier mod;
+ private:
+ using callback_t = double (*)(const accel_union&, const accel_args&, double, double);
+
+ void set_callback(callback_t& cb, accel_union& u, const accel_args& args)
+ {
+ u.visit([&](auto& impl) {
+ cb = &callback_template<remove_ref_t<decltype(impl)>>;
+ }, args);
+ }
+
+ template <typename AccelFunc>
+ static double callback_template(const accel_union& u,
+ const accel_args& args,
+ double x,
+ double range_weight)
+ {
+ auto& accel_fn = reinterpret_cast<const AccelFunc&>(u);
+ return 1 + (accel_fn(x, args) - 1) * range_weight;
+ }
+
+ bool apply_rotate = 0;
+ bool compute_ref_angle = 0;
+ bool apply_snap = 0;
+ bool clamp_speed = 0;
+ enum distance_mode : unsigned char {
+ separate,
+ max,
+ Lp,
+ euclidean,
+ } distance_mode = {};
+ bool apply_directional_weight = 0;
+ bool apply_dir_mul_x = 0;
+ bool apply_dir_mul_y = 0;
+
+ callback_t cb_x = &callback_template<accel_noaccel>;
+ callback_t cb_y = &callback_template<accel_noaccel>;
};
} // rawaccel
diff --git a/common/utility.hpp b/common/utility.hpp
index cbd19e3..63026c3 100644
--- a/common/utility.hpp
+++ b/common/utility.hpp
@@ -85,4 +85,10 @@ namespace rawaccel {
template <typename T, typename U>
inline constexpr bool is_same_v = is_same<T, U>::value;
+ template <class T> struct is_rvalue_ref { static constexpr bool value = false; };
+ template <class T> struct is_rvalue_ref<T&&> { static constexpr bool value = true; };
+
+ template <class T>
+ inline constexpr bool is_rvalue_ref_v = is_rvalue_ref<T>::value;
+
}