add independent xy accel to driver

other changes:

modifier_args type name is now settings,
which is now the type passed in driver ioctl

remove most settings/args verification from driver,
plan to let gui handle most of it

add another accel arg, rate, which is used to set
the 'accel' parameter of types which call exp (nat/sig),
might want to cap it

add (update) serializable DriverSettings (ModifierArgs) class to
gui and static methods for interop

remove properties from ManagedAccel, its now just a black box
for accessing modifier methods

add exception handling in wrapper_io to throw proper managed types

change SettingsManager::Startup to make a new settings file
if an error occurs during deserialization

change structure of accel types; how offset and weight are applied
now depend on additivity of types

remove tagged_union and add a handrolled variant/visit impl

AccelGui::UpdateActiveValueLabels currently broken for caps
and a few other args

remove gui default layout and initial natural accel setup

cli not updated
This commit is contained in:
a1xd 2020-08-31 19:41:21 -04:00
parent 313ab92531
commit 9010cc593a
42 changed files with 626 additions and 993 deletions

View file

@ -1,19 +1,5 @@
#pragma once
#include "vec2.h"
void bad_arg(const char*);
#ifndef _KERNEL_MODE
#include "rawaccel-error.hpp"
inline void bad_arg(const char* s) {
throw rawaccel::invalid_argument(s);
}
#endif
namespace rawaccel {
/// <summary> Struct to hold arguments for an acceleration function. </summary>
@ -24,55 +10,48 @@ namespace rawaccel {
double exponent = 2;
double midpoint = 0;
double power_scale = 1;
double power_exp = 0.05;
double weight = 1;
double rate = 0;
double scale_cap = 0;
double gain_cap = 0;
vec2d weight = { 1, 1 };
};
/// <summary>
/// Struct to hold common acceleration curve implementation details.
/// </summary>
struct accel_base {
template <typename Func>
struct accel_val_base {
double offset = 0;
double weight = 1;
Func fn;
/// <summary> Coefficients applied to acceleration per axis.</summary>
vec2d weight = { 1, 1 };
accel_val_base(const accel_args& args) : fn(args) {}
/// <summary> Generally, the acceleration ramp rate.</summary>
double speed_coeff = 0;
};
accel_base(const accel_args& args) {
verify(args);
template <typename Func>
struct additive_accel : accel_val_base<Func> {
speed_coeff = args.accel;
additive_accel(const accel_args& args) : accel_val_base(args) {
offset = args.offset;
weight = args.weight;
}
/// <summary>
/// Default transformation of speed -> acceleration.
/// </summary>
inline double accelerate(double speed) const {
return speed_coeff * speed;
inline double operator()(double speed) const {
return 1 + fn(maxsd(speed - offset, 0)) * weight;
}
/// <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
};
};
template <typename Func>
struct nonadditive_accel : accel_val_base<Func> {
nonadditive_accel(const accel_args& args) : accel_val_base(args) {
if (args.weight != 0) weight = args.weight;
}
/// <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) bad_arg("accel can not be negative, use a negative weight to compensate");
if (args.gain_cap > 0 && weight.x != weight.y) bad_arg("weight x and y values must be equal with a gain cap");
inline double operator()(double speed) const {
return fn(speed) * weight;
}
accel_base() = default;
};
}

View file

@ -7,23 +7,20 @@
namespace rawaccel {
/// <summary> Struct to hold "classic" (linear raised to power) acceleration implementation. </summary>
struct accel_classic : accel_base {
double exponent;
struct classic_impl {
double accel;
double power;
accel_classic(const accel_args& args) : accel_base(args) {
verify(args);
classic_impl(const accel_args& args) :
accel(args.accel), power(args.exponent - 1)
{}
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) bad_arg("exponent must be greater than 1");
inline double operator()(double speed) const {
//f(x) = (mx)^(k-1)
return pow(accel * speed, power);
}
};
using accel_classic = additive_accel<classic_impl>;
}

View file

@ -5,10 +5,17 @@
namespace rawaccel {
/// <summary> Struct to hold linear acceleration implementation. </summary>
struct accel_linear : accel_base {
struct linear_impl {
double accel;
linear_impl(const accel_args& args) : accel(args.accel) {}
using accel_base::accel_base;
inline double operator()(double speed) const {
return accel * speed;
}
};
using accel_linear = additive_accel<linear_impl>;
}

View file

@ -7,14 +7,16 @@
namespace rawaccel {
/// <summary> Struct to hold logarithmic acceleration implementation. </summary>
struct accel_logarithmic : accel_base {
struct logarithmic_impl {
double accel;
using accel_base::accel_base;
logarithmic_impl(const accel_args& args) : accel(args.accel) {}
inline double accelerate(double speed) const {
inline double operator()(double speed) const {
//f(x) = log(m*x+1)
return log(speed_coeff * speed + 1);
return log(accel * speed + 1);
}
};
using accel_logarithmic = additive_accel<logarithmic_impl>;
}

View file

@ -7,25 +7,23 @@
namespace rawaccel {
/// <summary> Struct to hold "natural" (vanishing difference) acceleration implementation. </summary>
struct accel_natural : accel_base {
double limit = 1;
double midpoint = 0;
struct natural_impl {
double rate;
double limit;
accel_natural(const accel_args& args) : accel_base(args) {
verify(args);
limit = args.limit - 1;
speed_coeff /= limit;
natural_impl(const accel_args& args) :
rate(args.accel), limit(args.limit - 1)
{
rate /= limit;
}
inline double accelerate(double speed) const {
inline double operator()(double speed) const {
// f(x) = k(1-e^(-mx))
return limit - (limit * exp(-speed_coeff * speed));
return limit - (limit * exp(-rate * speed));
}
void verify(const accel_args& args) const {
if (args.limit <= 1) bad_arg("limit must be greater than 1");
}
};
using accel_natural = additive_accel<natural_impl>;
}

View file

@ -2,31 +2,22 @@
#include <math.h>
#include "accel-base.hpp"
#include "accel-natural.hpp"
namespace rawaccel {
/// <summary> Struct to hold "natural" (vanishing difference) gain implementation. </summary>
struct accel_naturalgain : accel_base {
double limit = 1;
double midpoint = 0;
struct naturalgain_impl : natural_impl {
accel_naturalgain(const accel_args& args) : accel_base(args) {
verify(args);
using natural_impl::natural_impl;
limit = args.limit - 1;
speed_coeff /= limit;
}
inline double accelerate(double speed) const {
inline double operator()(double speed) const {
// f(x) = k((e^(-mx)-1)/mx + 1)
double scaled_speed = speed_coeff * speed;
double scaled_speed = rate * speed;
return limit * (((exp(-scaled_speed) - 1) / scaled_speed) + 1);
}
void verify(const accel_args& args) const {
if (args.limit <= 1) bad_arg("limit must be greater than 1");
}
};
using accel_naturalgain = additive_accel<naturalgain_impl>;
}

View file

@ -5,10 +5,13 @@
namespace rawaccel {
/// <summary> Struct to hold acceleration implementation which applies no acceleration. </summary>
struct accel_noaccel : accel_base {
struct accel_noaccel {
accel_noaccel(const accel_args&) : accel_base() {}
accel_noaccel(const accel_args&) {}
accel_noaccel() = default;
inline double operator()(double) const { return 1; }
};
}

View file

@ -7,35 +7,20 @@
namespace rawaccel {
/// <summary> Struct to hold power (non-additive) acceleration implementation. </summary>
struct accel_power : accel_base {
struct power_impl {
double scale;
double exponent;
double offset;
accel_power(const accel_args& args) {
verify(args);
power_impl(const accel_args& args) :
scale(args.power_scale), exponent(args.power_exp)
{}
weight = args.weight;
speed_coeff = args.power_scale;
exponent = args.exponent;
offset = args.offset;
}
inline double accelerate(double speed) const {
inline double operator()(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) bad_arg("scale must be positive");
if (args.exponent <= 0) bad_arg("exponent must be greater than 0");
return pow(speed * scale, exponent);
}
};
using accel_power = nonadditive_accel<power_impl>;
}

View file

@ -7,26 +7,22 @@
namespace rawaccel {
/// <summary> Struct to hold sigmoid (s-shaped) acceleration implementation. </summary>
struct accel_sigmoid : accel_base {
double limit = 1;
double midpoint = 0;
struct sigmoid_impl {
double rate;
double limit;
double midpoint;
accel_sigmoid(const accel_args& args) : accel_base(args) {
verify(args);
sigmoid_impl(const accel_args& args) :
rate(args.accel), limit(args.limit - 1), midpoint(args.midpoint)
{}
limit = args.limit - 1;
midpoint = args.midpoint;
}
inline double accelerate(double speed) const {
inline double operator()(double speed) const {
//f(x) = k/(1+e^(-m(x-c)))
return limit / (exp(-speed_coeff * (speed - midpoint)) + 1);
return limit / (exp(-rate * (speed - midpoint)) + 1);
}
void verify(const accel_args& args) const {
if (args.limit <= 1) bad_arg("exponent must be greater than 1");
if (args.midpoint < 0) bad_arg("midpoint must not be negative");
}
};
using accel_sigmoid = additive_accel<sigmoid_impl>;
}

View file

@ -7,31 +7,27 @@
namespace rawaccel {
/// <summary> Struct to hold sigmoid (s-shaped) gain implementation. </summary>
struct accel_sigmoidgain : accel_base {
double limit = 1;
double midpoint = 0;
double additive_constant = 0;
double integration_constant = 0;
struct sigmoidgain_impl {
double rate;
double limit;
double additive_constant;
double integration_constant;
accel_sigmoidgain(const accel_args& args) : accel_base(args) {
verify(args);
limit = args.limit - 1;
midpoint = args.midpoint;
additive_constant = exp(speed_coeff*midpoint);
sigmoidgain_impl(const accel_args& args) :
rate(args.rate), limit(args.limit - 1)
{
additive_constant = exp(rate * args.midpoint);
integration_constant = log(1 + additive_constant);
}
inline double accelerate(double speed) const {
inline double operator()(double speed) const {
//f(x) = k/(1+e^(-m(c-x)))
double scaled_speed = speed_coeff * speed;
double scaled_speed = rate * speed;
return limit * ((log(additive_constant+exp(scaled_speed)) - integration_constant)/scaled_speed);
}
void verify(const accel_args& args) const {
if (args.limit <= 1) bad_arg("exponent must be greater than 1");
if (args.midpoint < 0) bad_arg("midpoint must not be negative");
}
};
using accel_sigmoidgain = additive_accel<sigmoidgain_impl>;
}

View file

@ -26,8 +26,8 @@
<ClInclude Include="$(MSBuildThisFileDirectory)accel-sigmoidgain.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)rawaccel-error.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)rawaccel-io.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)rawaccel-settings.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)rawaccel.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)tagged-union-single.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)x64-util.hpp" />
<ClInclude Include="$(MSBuildThisFileDirectory)vec2.h" />
</ItemGroup>

View file

@ -5,7 +5,8 @@
#define NOMINMAX
#include <Windows.h>
#include "rawaccel.hpp"
#include "rawaccel-settings.h"
#include "rawaccel-error.hpp"
#define RA_READ CTL_CODE(0x8888, 0x888, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
#define RA_WRITE CTL_CODE(0x8888, 0x889, METHOD_BUFFERED, FILE_ANY_ACCESS)
@ -15,7 +16,7 @@
namespace rawaccel {
mouse_modifier read() {
settings read() {
HANDLE ra_handle = INVALID_HANDLE_VALUE;
ra_handle = CreateFileW(L"\\\\.\\rawaccel", 0, 0, 0, OPEN_EXISTING, 0, 0);
@ -24,7 +25,7 @@ namespace rawaccel {
throw install_error();
}
mouse_modifier mod;
settings args;
DWORD dummy;
BOOL success = DeviceIoControl(
@ -32,8 +33,8 @@ namespace rawaccel {
RA_READ,
NULL, // input buffer
0, // input buffer size
&mod, // output buffer
sizeof(mouse_modifier), // output buffer size
&args, // output buffer
sizeof(settings), // output buffer size
&dummy, // bytes returned
NULL // overlapped structure
);
@ -44,11 +45,11 @@ namespace rawaccel {
throw std::system_error(GetLastError(), std::system_category(), "DeviceIoControl failed");
}
return mod;
return args;
}
void write(const mouse_modifier& mod) {
void write(const settings& args) {
HANDLE ra_handle = INVALID_HANDLE_VALUE;
ra_handle = CreateFileW(L"\\\\.\\rawaccel", 0, 0, 0, OPEN_EXISTING, 0, 0);
@ -62,8 +63,8 @@ namespace rawaccel {
BOOL success = DeviceIoControl(
ra_handle,
RA_WRITE,
const_cast<mouse_modifier*>(&mod), // input buffer
sizeof(mouse_modifier), // input buffer size
const_cast<settings*>(&args), // input buffer
sizeof(settings), // input buffer size
NULL, // output buffer
0, // output buffer size
&dummy, // bytes returned

View file

@ -0,0 +1,21 @@
#pragma once
#include "vec2.h"
#include "accel-base.hpp"
namespace rawaccel {
enum class accel_mode {
linear, classic, natural, logarithmic, sigmoid, naturalgain, sigmoidgain, power, noaccel
};
struct settings {
double degrees_rotation = 0;
bool combine_mags = true;
vec2<accel_mode> modes = { accel_mode::noaccel, accel_mode::noaccel };
vec2<accel_args> argsv;
vec2d sens = { 1, 1 };
double time_min = 0.4;
};
}

View file

@ -3,8 +3,8 @@
#define _USE_MATH_DEFINES
#include <math.h>
#include "rawaccel-settings.h"
#include "x64-util.hpp"
#include "tagged-union-single.h"
#include "accel-linear.hpp"
#include "accel-classic.hpp"
@ -31,7 +31,7 @@ namespace rawaccel {
/// </summary>
/// <param name="input">Input vector to be rotated</param>
/// <returns>2d vector of rotated input.</returns>
inline vec2d operator()(const vec2d& input) const {
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
@ -60,7 +60,7 @@ namespace rawaccel {
return clampsd(scale, lo, hi);
}
accel_scale_clamp(double cap) : accel_scale_clamp() {
accel_scale_clamp(double cap) {
if (cap <= 0) {
// use default, effectively uncapped accel
return;
@ -76,9 +76,53 @@ namespace rawaccel {
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::logarithmic: return vis(var.u.logarithmic);
case accel_mode::sigmoid: return vis(var.u.sigmoid);
case accel_mode::naturalgain: return vis(var.u.naturalgain);
case accel_mode::sigmoidgain: return vis(var.u.sigmoidgain);
case accel_mode::power: return vis(var.u.power);
default: return vis(var.u.noaccel);
}
}
/// <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_naturalgain, accel_sigmoidgain, accel_noaccel>;
struct accel_variant {
accel_mode tag = accel_mode::noaccel;
union union_t {
accel_linear linear;
accel_classic classic;
accel_natural natural;
accel_logarithmic logarithmic;
accel_sigmoid sigmoid;
accel_naturalgain naturalgain;
accel_sigmoidgain sigmoidgain;
accel_power power;
accel_noaccel noaccel = {};
} u = {};
accel_variant(const accel_args& args, accel_mode mode) :
tag(mode)
{
visit_accel([&](auto& impl) {
impl = { args };
}, *this);
}
inline double apply(double speed) const {
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 {
@ -92,18 +136,14 @@ namespace rawaccel {
// <summary> The intercept for the line with above slope to give continuous velocity function </summary>
double intercept = 0;
// <summary> Whether or not velocity gain cap is enabled </summary>
bool cap_gain_enabled = false;
/// <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="offset"> The offset applied in accel calculations </param>
/// <param name="accel"> The accel implementation used in the containing accel_fn </param>
velocity_gain_cap(double speed, double offset, accel_impl_t accel)
/// <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;
@ -115,18 +155,9 @@ namespace rawaccel {
// Return if by glitch or strange values the difference in points is 0.
if (speed_diff == 0) return;
cap_gain_enabled = true;
// Find the corresponding output velocities for the two points.
// Subtract offset for acceleration, like in accel_fn()
double out_first = accel.visit([=](auto&& impl) {
double accel_val = impl.accelerate(speed-offset);
return impl.scale(accel_val);
}).x * speed;
double out_second = accel.visit([=](auto&& impl) {
double accel_val = impl.accelerate(speed_second-offset);
return impl.scale(accel_val);
}).x * speed_second;
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;
@ -141,7 +172,7 @@ namespace rawaccel {
/// </summary>
/// <param name="speed"> Speed to be capped </param>
/// <returns> Scale multiplier for input </returns>
inline double operator()(double speed) const {
inline double apply(double speed) const {
return slope + intercept / speed;
}
@ -151,166 +182,97 @@ namespace rawaccel {
/// <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 cap_gain_enabled && speed > threshold;
return threshold > 0 && speed > threshold;
}
velocity_gain_cap() = default;
};
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 };
};
struct accelerator {
accel_variant accel;
velocity_gain_cap gain_cap;
accel_scale_clamp clamp;
/// <summary> Struct for holding acceleration application details. </summary>
struct accel_function {
accelerator(const accel_args& args, accel_mode mode) :
accel(args, mode), gain_cap(args.gain_cap, accel), clamp(args.scale_cap)
{}
/*
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;
/// <summary> The offset past which acceleration is applied. </summary>
double speed_offset = 0;
/// <summary> The acceleration implementation (i.e. curve) </summary>
accel_impl_t accel;
/// <summary> The object which sets a min and max for the acceleration scale. </summary>
vec2<accel_scale_clamp> clamp;
velocity_gain_cap gain_cap = velocity_gain_cap();
accel_args impl_args;
accel_function(const accel_fn_args& args) {
if (args.time_min <= 0) bad_arg("min time must be positive");
if (args.acc_args.offset < 0) bad_arg("offset must not be negative");
accel.tag = args.accel_mode;
accel.visit([&](auto& impl) { impl = { args.acc_args }; });
impl_args = 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);
gain_cap = velocity_gain_cap(args.acc_args.gain_cap, speed_offset, accel);
inline double apply(double speed) const {
if (gain_cap.should_apply(speed)) {
return clamp(gain_cap.apply(speed));
}
else return clamp(accel.apply(speed));
}
/// <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 raw_speed = mag / time_clamped;
vec2d scale;
// gain_cap needs raw speed for velocity line calculation
if (gain_cap.should_apply(raw_speed))
{
double gain_cap_scale = gain_cap(raw_speed);
scale = { gain_cap_scale, gain_cap_scale };
}
else
{
scale = accel.visit([=](auto&& impl) {
double accel_val = impl.accelerate(maxsd(mag / time_clamped - speed_offset, 0));
return impl.scale(accel_val);
});
}
return {
input.x * clamp.x(scale.x),
input.y * clamp.y(scale.y)
};
}
accel_function() = default;
};
struct modifier_args {
double degrees = 0;
vec2d sens = { 1, 1 };
accel_fn_args acc_fn_args;
accelerator() = default;
};
/// <summary> Struct to hold variables and methods for modifying mouse input </summary>
struct mouse_modifier {
bool apply_rotate = false;
bool apply_accel = false;
bool combine_magnitudes = true;
rotator rotate;
accel_function accel_fn;
vec2<accelerator> accels;
vec2d sensitivity = { 1, 1 };
mouse_modifier(const modifier_args& args)
: accel_fn(args.acc_fn_args)
mouse_modifier(const settings& args) :
combine_magnitudes(args.combine_mags)
{
apply_rotate = args.degrees != 0;
if (args.degrees_rotation != 0) {
rotate = rotator(args.degrees_rotation);
apply_rotate = true;
}
if (args.sens.x != 0) sensitivity.x = args.sens.x;
if (args.sens.y != 0) sensitivity.y = args.sens.y;
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);
if ((combine_magnitudes && args.modes.x == accel_mode::noaccel) ||
(args.modes.x == accel_mode::noaccel &&
args.modes.y == accel_mode::noaccel)) {
return;
}
input.x *= sensitivity.x;
input.y *= sensitivity.y;
return input;
accels.x = accelerator(args.argsv.x, args.modes.x);
accels.y = accelerator(args.argsv.y, args.modes.y);
apply_accel = true;
}
/// <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);
void modify(vec2d& movement, milliseconds time) {
apply_rotation(movement);
apply_acceleration(movement, [=] { return time; });
apply_sensitivity(movement);
}
inline void apply_rotation(vec2d& movement) {
if (apply_rotate) movement = rotate.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 = sqrtsd(movement.x * movement.x + movement.y * movement.y);
double speed = mag / time;
double scale = accels.x.apply(speed);
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);
}
}
}
input = accel_fn(input, time);
input.x *= sensitivity.x;
input.y *= sensitivity.y;
return input;
inline void apply_sensitivity(vec2d& movement) {
movement.x *= sensitivity.x;
movement.y *= sensitivity.y;
}
mouse_modifier() = default;
};
} // rawaccel
} // rawaccel

View file

@ -1,202 +0,0 @@
#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...>;

View file

@ -14,9 +14,10 @@ namespace ra = rawaccel;
using milliseconds = double;
struct {
counter_t last_write = 0;
ra::settings args;
milliseconds tick_interval = 0; // set in DriverEntry
ra::mouse_modifier modifier;
counter_t last_write = 0;
} global;
VOID
@ -52,16 +53,12 @@ Arguments:
if (!(InputDataStart->Flags & MOUSE_MOVE_ABSOLUTE)) {
auto num_packets = InputDataEnd - InputDataStart;
// 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 local_apply_accel = num_packets == 1;
if (num_packets != 1) {
DebugPrint(("RA received %d packets\n", num_packets));
}
vec2d local_carry = devExt->carry;
bool enable_accel = num_packets == 1;
vec2d carry = devExt->carry;
auto it = InputDataStart;
do {
@ -70,39 +67,37 @@ Arguments:
static_cast<double>(it->LastY)
};
if (global.modifier.apply_accel && local_apply_accel) {
counter_t now = KeQueryPerformanceCounter(NULL).QuadPart;
counter_t ticks = now - devExt->counter;
devExt->counter = now;
global.modifier.apply_rotation(input);
milliseconds time = ticks * global.tick_interval;
if (time < global.modifier.accel_fn.time_min) {
DebugPrint(("RA time < min with %d ticks\n", ticks));
}
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);
};
input = global.modifier.modify_with_accel(input, time);
}
else
{
input = global.modifier.modify_without_accel(input);
global.modifier.apply_acceleration(input, time_supplier);
}
double result_x = input.x + local_carry.x;
double result_y = input.y + local_carry.y;
global.modifier.apply_sensitivity(input);
LONG out_x = static_cast<LONG>(result_x);
LONG out_y = static_cast<LONG>(result_y);
double carried_result_x = input.x + carry.x;
double carried_result_y = input.y + carry.y;
local_carry.x = result_x - out_x;
local_carry.y = result_y - out_y;
LONG out_x = static_cast<LONG>(carried_result_x);
LONG out_y = static_cast<LONG>(carried_result_y);
carry.x = carried_result_x - out_x;
carry.y = carried_result_y - out_y;
it->LastX = out_x;
it->LastY = out_y;
++it;
} while (it < InputDataEnd);
} while (++it != InputDataEnd);
devExt->carry = local_carry;
devExt->carry = carry;
}
(*(PSERVICE_CALLBACK_ROUTINE)devExt->UpperConnectData.ClassService)(
@ -154,7 +149,7 @@ Return Value:
DebugPrint(("Ioctl received into filter control object.\n"));
if (InputBufferLength == sizeof(ra::mouse_modifier)) {
if (InputBufferLength == sizeof(ra::settings)) {
constexpr milliseconds WRITE_COOLDOWN_TIME = 1000;
counter_t now = KeQueryPerformanceCounter(NULL).QuadPart;
@ -170,7 +165,7 @@ Return Value:
status = WdfRequestRetrieveInputBuffer(
Request,
sizeof(ra::mouse_modifier),
sizeof(ra::settings),
&buffer,
&size
);
@ -182,15 +177,16 @@ Return Value:
return;
}
global.modifier = *reinterpret_cast<ra::mouse_modifier*>(buffer);
global.args = *reinterpret_cast<ra::settings*>(buffer);
global.modifier = { global.args };
global.last_write = now;
WdfRequestComplete(Request, STATUS_SUCCESS);
}
else if (OutputBufferLength == sizeof(ra::mouse_modifier)) {
else if (OutputBufferLength == sizeof(ra::settings)) {
status = WdfRequestRetrieveOutputBuffer(
Request,
sizeof(ra::mouse_modifier),
sizeof(ra::settings),
&buffer,
&size
);
@ -202,7 +198,7 @@ Return Value:
return;
}
*reinterpret_cast<ra::mouse_modifier*>(buffer) = global.modifier;
*reinterpret_cast<ra::settings*>(buffer) = global.args;
WdfRequestComplete(Request, STATUS_SUCCESS);
}

View file

@ -15,48 +15,6 @@ using grapher.Models.Serialized;
namespace grapher
{
public enum accel_mode
{
linear=1, classic, natural, logarithmic, sigmoid, power, naturalgain, sigmoidgain, 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 double gain_cap;
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
{
@ -66,32 +24,17 @@ namespace grapher
public RawAcceleration()
{
InitializeComponent();
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.acc_args.gain_cap = 0;
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 managedAcceleration = new ManagedAccel(args_ptr);
Marshal.FreeHGlobal(args_ptr);
ManagedAccel activeAccel = null;
try
{
activeAccel = ManagedAccel.GetActiveAccel();
}
catch (DriverNotInstalledException)
{
throw;
}
var accelCharts = new AccelCharts(
this,
@ -205,7 +148,7 @@ namespace grapher
new Field(PollRateTextBox.TextBox, this, AccelCalculator.DefaultPollRate));
var settings = new SettingsManager(
managedAcceleration,
activeAccel,
accelCalculator.DPI,
accelCalculator.PollRate,
AutoWriteMenuItem);

View file

@ -1,4 +1,5 @@
using System;
using grapher.Models.Serialized;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -12,7 +13,7 @@ namespace grapher.Layouts
: base()
{
Name = "Classic";
Index = 2;
Index = (int)AccelMode.classic;
ShowOptions = new bool[] { true, true, true, false };
OptionNames = new string[] { Offset, Acceleration, Exponent, string.Empty };
}

View file

@ -1,10 +1,12 @@
using System;
using grapher.Models.Serialized;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace grapher.Layouts
{
public class DefaultLayout : LayoutBase
@ -13,7 +15,7 @@ namespace grapher.Layouts
: base()
{
Name = "Default";
Index = 0;
Index = (int)AccelMode.noaccel;
ShowOptions = new bool[] { true, true, true, true };
OptionNames = new string[] { Offset, Acceleration, $"{Limit}\\{Exponent}", Midpoint };
ButtonEnabled = false;

View file

@ -26,7 +26,7 @@ namespace grapher.Layouts
/// <summary>
/// Gets or sets mapping from acceleration type to identifying integer.
/// Must match order in tagged_union in rawaccel.hpp (which is 1-indexed, meaning 0 is off.)
/// Must match accel_mode defined in rawaccel-settings.h
/// </summary>
public int Index { get; internal set; }

View file

@ -1,4 +1,5 @@
using System;
using grapher.Models.Serialized;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -12,7 +13,7 @@ namespace grapher.Layouts
: base()
{
Name = "Linear";
Index = 1;
Index = (int)AccelMode.linear;
ShowOptions = new bool[] { true, true, false, false };
OptionNames = new string[] { Offset, Acceleration, string.Empty, string.Empty };
}

View file

@ -1,4 +1,5 @@
using System;
using grapher.Models.Serialized;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -12,7 +13,7 @@ namespace grapher.Layouts
: base()
{
Name = "Logarithmic";
Index = 4;
Index = (int)AccelMode.logarithmic;
ShowOptions = new bool[] { true, true, false, false };
OptionNames = new string[] { Offset, Acceleration, string.Empty, string.Empty };
}

View file

@ -1,4 +1,5 @@
using System;
using grapher.Models.Serialized;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -12,7 +13,7 @@ namespace grapher.Layouts
: base()
{
Name = "NaturalGain";
Index = 7;
Index = (int)AccelMode.naturalgain;
ShowOptions = new bool[] { true, true, true, false };
OptionNames = new string[] { Offset, Acceleration, Limit, string.Empty };
}

View file

@ -1,4 +1,5 @@
using System;
using grapher.Models.Serialized;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -12,7 +13,7 @@ namespace grapher.Layouts
: base()
{
Name = "Natural";
Index = 3;
Index = (int)AccelMode.natural;
ShowOptions = new bool[] { true, true, true, false };
OptionNames = new string[] { Offset, Acceleration, Limit, string.Empty };
}

View file

@ -1,4 +1,5 @@
using System;
using grapher.Models.Serialized;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -12,7 +13,7 @@ namespace grapher.Layouts
: base()
{
Name = "Off";
Index = 9;
Index = (int)AccelMode.noaccel;
ShowOptions = new bool[] { false, false, false, false };
OptionNames = new string[] { string.Empty, string.Empty, string.Empty, string.Empty };
ShowOptionsXY = new bool[] { false, false };

View file

@ -1,4 +1,5 @@
using System;
using grapher.Models.Serialized;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -12,7 +13,7 @@ namespace grapher.Layouts
: base()
{
Name = "Power";
Index = 6;
Index = (int)AccelMode.power;
ShowOptions = new bool[] { true, true, true, false };
OptionNames = new string[] { Offset, Scale, Exponent, string.Empty };
}

View file

@ -1,4 +1,5 @@
using System;
using grapher.Models.Serialized;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -12,7 +13,7 @@ namespace grapher.Layouts
: base()
{
Name = "SigmoidGain";
Index = 8;
Index = (int)AccelMode.sigmoidgain;
ShowOptions = new bool[] { true, true, true, true };
OptionNames = new string[] { Offset, Acceleration, Limit, Midpoint };
}

View file

@ -1,4 +1,5 @@
using System;
using grapher.Models.Serialized;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -12,7 +13,7 @@ namespace grapher.Layouts
: base()
{
Name = "Sigmoid";
Index = 5;
Index = (int)AccelMode.sigmoid;
ShowOptions = new bool[] { true, true, true, true };
OptionNames = new string[] { Offset, Acceleration, Limit, Midpoint };
}

View file

@ -2,6 +2,7 @@
using grapher.Models.Mouse;
using grapher.Models.Serialized;
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
@ -101,41 +102,64 @@ namespace grapher
public void UpdateActiveSettingsFromFields()
{
Settings.UpdateActiveSettings(
AccelerationOptions.AccelerationIndex,
Rotation.Field.Data,
Sensitivity.Fields.X,
Sensitivity.Fields.Y,
Weight.Fields.X,
Weight.Fields.Y,
Cap.SensitivityCapX,
Cap.SensitivityCapY,
Offset.Field.Data,
Acceleration.Field.Data,
LimitOrExponent.Field.Data,
Midpoint.Field.Data,
Cap.VelocityGainCap);
Settings.UpdateActiveSettings(new DriverSettings
{
rotation = Rotation.Field.Data,
sensitivity = new Vec2<double>
{
x = Sensitivity.Fields.X,
y = Sensitivity.Fields.Y
},
combineMagnitudes = true,
modes = new Vec2<AccelMode>
{
x = (AccelMode)AccelerationOptions.AccelerationIndex
},
args = new Vec2<AccelArgs>
{
x = new AccelArgs
{
offset = Offset.Field.Data,
weight = Weight.Fields.X,
gainCap = Cap.VelocityGainCap,
scaleCap = Cap.SensitivityCapX,
accel = Acceleration.Field.Data,
rate = Acceleration.Field.Data,
powerScale = Acceleration.Field.Data,
limit = LimitOrExponent.Field.Data,
exponent = LimitOrExponent.Field.Data,
powerExponent = LimitOrExponent.Field.Data,
midpoint = Midpoint.Field.Data
}
},
minimumTime = .4
});
UpdateGraph();
}
public void UpdateGraph()
{
AccelCalculator.Calculate(AccelCharts.AccelData, Settings.ActiveAccel);
AccelCalculator.Calculate(
AccelCharts.AccelData,
Settings.ActiveAccel,
Settings.RawAccelSettings.AccelerationSettings);
AccelCharts.Bind();
UpdateActiveValueLabels();
}
public void UpdateActiveValueLabels()
{
Sensitivity.SetActiveValues(Settings.ActiveAccel.SensitivityX, Settings.ActiveAccel.SensitivityY);
Rotation.SetActiveValue(Settings.ActiveAccel.Rotation);
AccelerationOptions.SetActiveValue(Settings.ActiveAccel.Type);
Offset.SetActiveValue(Settings.ActiveAccel.Offset);
Acceleration.SetActiveValue(Settings.ActiveAccel.Acceleration);
Cap.SetActiveValues(Settings.ActiveAccel.GainCap, Settings.ActiveAccel.CapX, Settings.ActiveAccel.CapY, Settings.ActiveAccel.GainCapEnabled);
Weight.SetActiveValues(Settings.ActiveAccel.WeightX, Settings.ActiveAccel.WeightY);
LimitOrExponent.SetActiveValue(Settings.ActiveAccel.LimitExp);
Midpoint.SetActiveValue(Settings.ActiveAccel.Midpoint);
var settings = Settings.RawAccelSettings.AccelerationSettings;
Sensitivity.SetActiveValues(settings.sensitivity.x, settings.sensitivity.y);
Rotation.SetActiveValue(settings.rotation);
AccelerationOptions.SetActiveValue((int)settings.modes.x);
Offset.SetActiveValue(settings.args.x.offset);
Weight.SetActiveValues(settings.args.x.weight, settings.args.x.weight);
Acceleration.SetActiveValue(settings.args.x.accel); // rate, powerscale
LimitOrExponent.SetActiveValue(settings.args.x.limit); //exp, powerexp
Midpoint.SetActiveValue(settings.args.x.midpoint);
//Cap.SetActiveValues(Settings.ActiveAccel.GainCap, Settings.ActiveAccel.CapX, Settings.ActiveAccel.CapY, Settings.ActiveAccel.GainCapEnabled);
}
private void OnScaleMenuItemClick(object sender, EventArgs e)

View file

@ -1,4 +1,5 @@
using System;
using grapher.Models.Serialized;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
@ -46,15 +47,15 @@ namespace grapher.Models.Calculations
private int Increment { get; set; }
public void Calculate(AccelData data, ManagedAccel accel)
public void Calculate(AccelData data, ManagedAccel accel, DriverSettings settings)
{
ScaleByMouseSettings();
data.Clear();
Calculate(data.Combined, accel, accel.SensitivityX, MagnitudesCombined);
Calculate(data.X, accel, accel.SensitivityX, MagnitudesX);
Calculate(data.Y, accel, accel.SensitivityY, MagnitudesY);
Calculate(data.Combined, accel, settings.sensitivity.x, MagnitudesCombined);
Calculate(data.X, accel, settings.sensitivity.x, MagnitudesX);
Calculate(data.Y, accel, settings.sensitivity.y, MagnitudesY);
}
public static void Calculate(AccelChartData data, ManagedAccel accel, double starter, ICollection<MagnitudeData> magnitudeData)

View file

@ -16,7 +16,6 @@ namespace grapher
public static readonly Dictionary<string, LayoutBase> AccelerationTypes = new List<LayoutBase>
{
new DefaultLayout(),
new LinearLayout(),
new ClassicLayout(),
new NaturalLayout(),
@ -37,7 +36,7 @@ namespace grapher
{
AccelDropdown = accelDropdown;
AccelDropdown.Items.Clear();
AccelDropdown.Items.AddRange(AccelerationTypes.Keys.Skip(1).ToArray());
AccelDropdown.Items.AddRange(AccelerationTypes.Keys.ToArray());
AccelDropdown.SelectedIndexChanged += new System.EventHandler(OnIndexChanged);
if (options.Length > PossibleOptionsCount)
@ -55,7 +54,7 @@ namespace grapher
WriteButton = writeButton;
ActiveValueLabel = activeValueLabel;
Layout("Default");
Layout("Off");
}
public Button WriteButton { get; }

View file

@ -0,0 +1,87 @@
using System;
using System.Runtime.InteropServices;
namespace grapher.Models.Serialized
{
public enum AccelMode
{
linear, classic, natural, logarithmic, sigmoid, naturalgain, sigmoidgain, power, noaccel
}
[StructLayout(LayoutKind.Sequential)]
public struct AccelArgs
{
public double offset;
public double accel;
public double limit;
public double exponent;
public double midpoint;
public double powerScale;
public double powerExponent;
public double weight;
public double rate;
public double scaleCap;
public double gainCap;
};
[StructLayout(LayoutKind.Sequential)]
public struct Vec2 <T>
{
public T x;
public T y;
}
[StructLayout(LayoutKind.Sequential)]
[Serializable]
public class DriverSettings
{
private static readonly IntPtr UnmanagedSettingsHandle =
Marshal.AllocHGlobal(Marshal.SizeOf<DriverSettings>());
public double rotation;
public bool combineMagnitudes;
public Vec2<AccelMode> modes;
public Vec2<AccelArgs> args;
public Vec2<double> sensitivity;
public double minimumTime;
public static DriverSettings GetActive()
{
DriverInterop.GetActiveSettings(UnmanagedSettingsHandle);
return Marshal.PtrToStructure<DriverSettings>(UnmanagedSettingsHandle);
}
public static void SetActive(DriverSettings settings)
{
Marshal.StructureToPtr(settings, UnmanagedSettingsHandle, false);
DriverInterop.SetActiveSettings(UnmanagedSettingsHandle);
}
public void SendToDriver()
{
SetActive(this);
}
public void SendToDriverAndUpdate(ManagedAccel accel)
{
SendToDriver();
accel.UpdateFromSettings(UnmanagedSettingsHandle);
}
public bool verify()
{
/*
if (args.accel < 0 || args.rate < 0)
bad_arg("accel can not be negative, use a negative weight to compensate");
if (args.rate > 1) bad_arg("rate can not be greater than 1");
if (args.exponent <= 1) bad_arg("exponent must be greater than 1");
if (args.limit <= 1) bad_arg("limit must be greater than 1");
if (args.power_scale <= 0) bad_arg("scale must be positive");
if (args.power_exp <= 0) bad_arg("exponent must be positive");
if (args.midpoint < 0) bad_arg("midpoint must not be negative");
if (args.time_min <= 0) bad_arg("min time must be positive");
*/
return true;
}
}
}

View file

@ -1,75 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace grapher.Models.Serialized
{
public enum accel_mode
{
linear=1, classic, natural, logarithmic, sigmoid, power, naturalgain, sigmoidgain, noaccel
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
[Serializable]
public struct vec2d
{
public double x;
public double y;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
[Serializable]
public struct accel_args
{
public double offset;
public double accel;
public double limit;
public double exponent;
public double midpoint;
public double power_scale;
public double gain_cap;
public vec2d weight;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
[Serializable]
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)]
[Serializable]
public struct modifier_args
{
public double degrees;
public vec2d sens;
public accel_fn_args acc_fn_args;
public modifier_args(ManagedAccel managedAccel)
{
degrees = managedAccel.Rotation;
sens.x = managedAccel.SensitivityX;
sens.y = managedAccel.SensitivityY;
acc_fn_args.accel_mode = managedAccel.Type;
acc_fn_args.time_min = managedAccel.MinimumTime;
acc_fn_args.cap.x = managedAccel.CapX;
acc_fn_args.cap.y = managedAccel.CapY;
acc_fn_args.acc_args.accel = managedAccel.Acceleration;
acc_fn_args.acc_args.exponent = managedAccel.LimitExp;
acc_fn_args.acc_args.gain_cap = managedAccel.GainCap;
acc_fn_args.acc_args.limit = managedAccel.LimitExp;
acc_fn_args.acc_args.midpoint = managedAccel.Midpoint;
acc_fn_args.acc_args.offset = managedAccel.Offset;
acc_fn_args.acc_args.power_scale = managedAccel.PowerScale;
acc_fn_args.acc_args.weight.x = managedAccel.WeightX;
acc_fn_args.acc_args.weight.y = managedAccel.WeightY;
}
}
}

View file

@ -24,17 +24,16 @@ namespace grapher.Models.Serialized
public RawAccelSettings() { }
public RawAccelSettings(
ManagedAccel managedAccel,
DriverSettings accelSettings,
GUISettings guiSettings)
{
AccelerationSettings = new modifier_args(managedAccel);
AccelerationSettings = accelSettings;
GUISettings = guiSettings;
}
public GUISettings GUISettings { get; set; }
public modifier_args AccelerationSettings { get; set; }
public DriverSettings AccelerationSettings { get; set; }
public static RawAccelSettings Load()
{
@ -42,23 +41,19 @@ namespace grapher.Models.Serialized
}
public static RawAccelSettings Load(string file)
{
if (!Exists(file))
{
throw new Exception($"Settings file does not exist at {file}");
}
RawAccelSettings deserializedSettings;
{
try
{
deserializedSettings = JsonConvert.DeserializeObject<RawAccelSettings>(File.ReadAllText(file), SerializerSettings);
return JsonConvert.DeserializeObject<RawAccelSettings>(File.ReadAllText(file), SerializerSettings);
}
catch(Exception e)
catch (FileNotFoundException e)
{
throw new Exception($"Settings file at {file} does not contain valid Raw Accel Settings.", e);
throw new FileNotFoundException($"Settings file does not exist at {file}", e);
}
catch (JsonSerializationException e)
{
throw new JsonSerializationException($"Settings file at {file} does not contain valid Raw Accel Settings.", e);
}
return deserializedSettings;
}
public static bool Exists()

View file

@ -1,9 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System;
using System.Windows.Forms;
namespace grapher.Models.Serialized
@ -32,63 +28,38 @@ namespace grapher.Models.Serialized
private ToolStripMenuItem AutoWriteMenuItem { get; set; }
public void UpdateActiveSettings(
int mode,
double degrees,
double sensitivityX,
double sensitivityY,
double weightX,
double weightY,
double capX,
double capY,
double offset,
double accel,
double limitOrExp,
double midpoint,
double gainCap)
public void UpdateActiveSettings(DriverSettings settings)
{
ActiveAccel.UpdateAccel(
mode,
degrees,
sensitivityX,
sensitivityY,
weightX,
weightY,
capX,
capY,
offset,
accel,
limitOrExp,
midpoint,
gainCap);
try
{
settings.SendToDriverAndUpdate(ActiveAccel);
RawAccelSettings.AccelerationSettings = new modifier_args(ActiveAccel);
RawAccelSettings.GUISettings = new GUISettings
RawAccelSettings.AccelerationSettings = settings;
RawAccelSettings.GUISettings = new GUISettings
{
AutoWriteToDriverOnStartup = AutoWriteMenuItem.Checked,
DPI = (int)DpiField.Data,
PollRate = (int)PollRateField.Data
};
RawAccelSettings.Save();
RawAccelSettings.Save();
}
catch (DriverWriteCDException)
{
Console.WriteLine("write on cooldown");
}
}
public void UpdateActiveAccelFromFileSettings()
public void UpdateActiveAccelFromFileSettings(DriverSettings settings)
{
ActiveAccel.UpdateAccel(
RawAccelSettings.AccelerationSettings.acc_fn_args.accel_mode,
RawAccelSettings.AccelerationSettings.degrees,
RawAccelSettings.AccelerationSettings.sens.x,
RawAccelSettings.AccelerationSettings.sens.y,
RawAccelSettings.AccelerationSettings.acc_fn_args.acc_args.weight.x,
RawAccelSettings.AccelerationSettings.acc_fn_args.acc_args.weight.y,
RawAccelSettings.AccelerationSettings.acc_fn_args.cap.x,
RawAccelSettings.AccelerationSettings.acc_fn_args.cap.y,
RawAccelSettings.AccelerationSettings.acc_fn_args.acc_args.offset,
RawAccelSettings.AccelerationSettings.acc_fn_args.acc_args.accel,
RawAccelSettings.AccelerationSettings.acc_fn_args.acc_args.exponent,
RawAccelSettings.AccelerationSettings.acc_fn_args.acc_args.midpoint,
RawAccelSettings.AccelerationSettings.acc_fn_args.acc_args.gain_cap);
try
{
settings.SendToDriverAndUpdate(ActiveAccel);
}
catch (DriverWriteCDException)
{
Console.WriteLine("write on cd during file init");
}
DpiField.SetToEntered(RawAccelSettings.GUISettings.DPI);
PollRateField.SetToEntered(RawAccelSettings.GUISettings.PollRate);
AutoWriteMenuItem.Checked = RawAccelSettings.GUISettings.AutoWriteToDriverOnStartup;
@ -96,28 +67,32 @@ namespace grapher.Models.Serialized
public void Startup()
{
ActiveAccel.ReadFromDriver();
if(RawAccelSettings.Exists())
if (RawAccelSettings.Exists())
{
RawAccelSettings = RawAccelSettings.Load();
if (RawAccelSettings.GUISettings.AutoWriteToDriverOnStartup)
try
{
UpdateActiveAccelFromFileSettings();
RawAccelSettings = RawAccelSettings.Load();
if (RawAccelSettings.GUISettings.AutoWriteToDriverOnStartup)
{
UpdateActiveAccelFromFileSettings(RawAccelSettings.AccelerationSettings);
}
return;
}
catch (JsonSerializationException e)
{
Console.WriteLine($"bad settings: {e}");
}
}
else
{
RawAccelSettings = new RawAccelSettings(
ActiveAccel,
new GUISettings
{
AutoWriteToDriverOnStartup = AutoWriteMenuItem.Checked,
DPI = (int)DpiField.Data,
PollRate = (int)PollRateField.Data
});
RawAccelSettings.Save();
}
RawAccelSettings = new RawAccelSettings(
DriverSettings.GetActive(),
new GUISettings
{
AutoWriteToDriverOnStartup = AutoWriteMenuItem.Checked,
DPI = (int)DpiField.Data,
PollRate = (int)PollRateField.Data
});
RawAccelSettings.Save();
}
}
}

View file

@ -88,7 +88,7 @@
<Compile Include="Models\Options\Option.cs" />
<Compile Include="Models\Options\OptionXY.cs" />
<Compile Include="Models\Serialized\GUISettings.cs" />
<Compile Include="Models\Serialized\ModifierArgs.cs" />
<Compile Include="Models\Serialized\DriverSettings.cs" />
<Compile Include="Models\Serialized\RawAccelSettings.cs" />
<Compile Include="Models\Serialized\SettingsManager.cs" />
<Compile Include="Program.cs" />

View file

@ -1,80 +1,62 @@
#pragma once
#include "wrapper.hpp"
#include <rawaccel.hpp>
void replace(mouse_modifier* mod_ptr, const modifier_args& args) {
*mod_ptr = mouse_modifier(args);
}
#include "wrapper_io.hpp"
Tuple<double, double>^ ManagedAccel::Accelerate(int x, int y, double time)
using namespace System;
public ref struct DriverInterop
{
vec2d input_vec2d = {
(double)x,
(double)y
};
vec2d output = modifier_instance->modify_with_accel(input_vec2d, time);
static void GetActiveSettings(IntPtr argsOut)
{
wrapper_io::readFromDriver(*reinterpret_cast<settings*>(argsOut.ToPointer()));
}
return gcnew Tuple<double, double>(output.x, output.y);
}
static void SetActiveSettings(IntPtr argsIn)
{
wrapper_io::writeToDriver(*reinterpret_cast<settings*>(argsIn.ToPointer()));
}
};
void ManagedAccel::UpdateAccel(
int mode,
double rotation,
double sensitivityX,
double sensitivityY,
double weightX,
double weightY,
double capX,
double capY,
double offset,
double accel,
double lim_exp,
double midpoint,
double gain_cap)
public ref class ManagedAccel
{
modifier_args args{};
args.acc_fn_args.accel_mode = mode;
args.degrees = rotation;
args.sens.x = sensitivityX;
args.sens.y = sensitivityY;
args.acc_fn_args.acc_args.weight.x = weightX;
args.acc_fn_args.acc_args.weight.y = weightY;
args.acc_fn_args.cap.x = capX;
args.acc_fn_args.cap.y = capY;
args.acc_fn_args.acc_args.offset = offset;
args.acc_fn_args.acc_args.accel = accel;
args.acc_fn_args.acc_args.limit = lim_exp;
args.acc_fn_args.acc_args.exponent = lim_exp;
args.acc_fn_args.acc_args.midpoint = midpoint;
args.acc_fn_args.acc_args.gain_cap = gain_cap;
mouse_modifier* const modifier_instance = new mouse_modifier();
replace(modifier_instance, args);
WriteToDriver();
}
public:
virtual ~ManagedAccel()
{
delete modifier_instance;
}
double ManagedAccel::SensitivityX::get() { return modifier_instance->sensitivity.x; }
double ManagedAccel::SensitivityY::get() { return modifier_instance->sensitivity.y; }
double ManagedAccel::Rotation::get() { return atan(modifier_instance->rotate.rot_vec.y / modifier_instance->rotate.rot_vec.x) * 180 / M_PI; }
int ManagedAccel::Type::get() { return modifier_instance->accel_fn.accel.tag; }
double ManagedAccel::Acceleration::get() { return modifier_instance->accel_fn.impl_args.accel; }
double ManagedAccel::CapX::get() { return modifier_instance->accel_fn.clamp.x.hi; }
double ManagedAccel::CapY::get() { return modifier_instance->accel_fn.clamp.y.hi; }
double ManagedAccel::GainCap::get() { return modifier_instance->accel_fn.gain_cap.threshold; }
bool ManagedAccel::GainCapEnabled::get() { return modifier_instance->accel_fn.gain_cap.cap_gain_enabled; }
double ManagedAccel::WeightX::get() { return modifier_instance->accel_fn.impl_args.weight.x; }
double ManagedAccel::WeightY::get() { return modifier_instance->accel_fn.impl_args.weight.y; }
double ManagedAccel::Offset::get() { return modifier_instance->accel_fn.speed_offset; }
double ManagedAccel::LimitExp::get() { return modifier_instance->accel_fn.impl_args.limit; }
double ManagedAccel::Midpoint::get() { return modifier_instance->accel_fn.impl_args.midpoint; }
double ManagedAccel::MinimumTime::get() { return modifier_instance->accel_fn.time_min; }
double ManagedAccel::PowerScale::get() { return modifier_instance->accel_fn.impl_args.power_scale; }
!ManagedAccel()
{
delete modifier_instance;
}
void ManagedAccel::WriteToDriver()
{
wrapper_io::writeToDriver(*modifier_instance);
}
Tuple<double, double>^ Accelerate(int x, int y, double time)
{
vec2d in_out_vec = {
(double)x,
(double)y
};
modifier_instance->modify(in_out_vec, time);
void ManagedAccel::ReadFromDriver()
{
wrapper_io::readFromDriver(*modifier_instance);
}
return gcnew Tuple<double, double>(in_out_vec.x, in_out_vec.y);
}
void UpdateFromSettings(IntPtr argsIn)
{
*modifier_instance = { *reinterpret_cast<settings*>(argsIn.ToPointer()) };
}
static ManagedAccel^ GetActiveAccel()
{
settings args;
wrapper_io::readFromDriver(args);
auto active = gcnew ManagedAccel();
*active->modifier_instance = { args };
return active;
}
};

View file

@ -1,74 +0,0 @@
#pragma once
#include "wrapper_io.hpp"
using namespace System;
public ref class ManagedAccel
{
mouse_modifier* const modifier_instance;
public:
ManagedAccel(System::IntPtr args) :
modifier_instance(new mouse_modifier(*reinterpret_cast<modifier_args*>(args.ToPointer())))
{}
// Empty constructor needed for serialization
ManagedAccel() : modifier_instance(nullptr) {}
virtual ~ManagedAccel()
{
if (modifier_instance != nullptr)
{
delete modifier_instance;
}
}
!ManagedAccel()
{
if (modifier_instance != nullptr)
{
delete modifier_instance;
}
}
// Duplicate all relevant rawaccel struct members here for access and display in GUI
property double SensitivityX { double get(); }
property double SensitivityY { double get(); }
property double Rotation { double get(); }
property int Type { int get(); }
property double Acceleration { double get(); }
property bool GainCapEnabled { bool get(); }
property double CapX { double get(); }
property double CapY { double get(); }
property double GainCap { double get(); }
property double WeightX { double get(); }
property double WeightY { double get(); }
property double Offset { double get(); }
property double LimitExp { double get(); }
property double Midpoint { double get(); }
property double MinimumTime { double get(); }
property double PowerScale { double get(); }
Tuple<double, double>^ Accelerate(int x, int y, double time);
void UpdateAccel(
int mode,
double rotation,
double sensitivityX,
double sensitivityY,
double weightX,
double weightY,
double capX,
double capY,
double offset,
double accel,
double lim_exp,
double midpoint,
double gain_cap);
void WriteToDriver();
void ReadFromDriver();
};

View file

@ -68,7 +68,6 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="wrapper.hpp" />
<ClInclude Include="wrapper_io.hpp" />
</ItemGroup>
<ItemGroup>

View file

@ -15,9 +15,6 @@
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="wrapper.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="wrapper_io.hpp">
<Filter>Header Files</Filter>
</ClInclude>

View file

@ -3,12 +3,38 @@
#include <rawaccel-io.hpp>
#include "wrapper_io.hpp"
void wrapper_io::writeToDriver(const mouse_modifier& modifier)
void wrapper_io::writeToDriver(const settings& args)
{
write(modifier);
try
{
write(args);
}
catch (const cooldown_error&)
{
throw gcnew DriverWriteCDException();
}
catch (const install_error&)
{
throw gcnew DriverNotInstalledException();
}
catch (const std::system_error& e)
{
throw gcnew DriverIOException(gcnew String(e.what()));
}
}
void wrapper_io::readFromDriver(mouse_modifier& modifier)
void wrapper_io::readFromDriver(settings& args)
{
modifier = read();
try
{
args = read();
}
catch (const install_error&)
{
throw gcnew DriverNotInstalledException();
}
catch (const std::system_error& e)
{
throw gcnew DriverIOException(gcnew String(e.what()));
}
}

View file

@ -1,10 +1,21 @@
#pragma once
#include <rawaccel.hpp>
#include <rawaccel-settings.h>
using namespace rawaccel;
using namespace System;
struct wrapper_io {
static void writeToDriver(const mouse_modifier& modifier);
static void readFromDriver(mouse_modifier& modifier);
static void writeToDriver(const settings&);
static void readFromDriver(settings&);
};
public ref struct DriverIOException : public IO::IOException {
public:
DriverIOException() {}
DriverIOException(String^ what) : IO::IOException(what) {}
};
public ref struct DriverNotInstalledException : public DriverIOException {};
public ref struct DriverWriteCDException : public DriverIOException {};