├── .gitignore ├── FUNDING.yml ├── LICENSE ├── Makefile ├── Performance.md ├── README.org ├── debug ├── Readme.org ├── devices │ ├── coolermaster_mm710_descriptor.txt │ ├── coolermaster_mm710_descriptor_raw.txt │ ├── csl_optical_mouse_descriptor.txt │ ├── csl_optical_mouse_descriptor_raw.txt │ ├── logitech_g5.txt │ ├── logitech_g5_descriptor.txt │ ├── logitech_g5_descriptor_raw.txt │ ├── packets │ │ ├── csl_optical_mouse.txt │ │ └── steelseries_rival_600.txt │ ├── steelseries_kana.txt │ ├── steelseries_kana_descriptor.txt │ ├── steelseries_kana_descriptor_raw.txt │ ├── steelseries_rival600.txt │ ├── steelseries_rival600_descriptor.txt │ ├── steelseries_rival600_descriptor_raw.txt │ ├── swiftpoint_tracer_descriptor.txt │ ├── swiftpoint_tracer_descriptor_raw.txt │ ├── trust_gxt_101_gav.txt │ ├── trust_gxt_101_gav_descriptor.txt │ └── trust_gxt_101_gav_descriptor_raw.txt └── hid_parser │ ├── Readme.org │ ├── hid_parser.cpp │ └── hid_parser.h ├── driver ├── FixedMath │ ├── Fixed64.h │ └── FixedUtil.h ├── Makefile ├── accel.c ├── accel.h ├── accel_modes.c ├── accel_modes.h ├── config.sample.h ├── driver.c └── util.h ├── gui ├── ConfigHelper.cpp ├── ConfigHelper.h ├── CustomCurve.cpp ├── CustomCurve.h ├── DriverHelper.cpp ├── DriverHelper.h ├── External │ ├── FixedMath │ │ ├── Fixed64.h │ │ └── FixedUtil.h │ └── ImGui │ │ ├── imconfig.h │ │ ├── imgui.cpp │ │ ├── imgui.h │ │ ├── imgui_demo.cpp │ │ ├── imgui_draw.cpp │ │ ├── imgui_impl_glfw.cpp │ │ ├── imgui_impl_glfw.h │ │ ├── imgui_impl_opengl3.cpp │ │ ├── imgui_impl_opengl3.h │ │ ├── imgui_impl_opengl3_loader.h │ │ ├── imgui_internal.h │ │ ├── imgui_tables.cpp │ │ ├── imgui_widgets.cpp │ │ ├── implot.cpp │ │ ├── implot.h │ │ ├── implot_demo.cpp │ │ ├── implot_internal.h │ │ ├── implot_items.cpp │ │ ├── imstb_rectpack.h │ │ ├── imstb_textedit.h │ │ └── imstb_truetype.h ├── FunctionHelper.cpp ├── FunctionHelper.h ├── ImGuiExtensions.cpp ├── ImGuiExtensions.h ├── Makefile ├── fonts.h ├── gui.cpp ├── gui.h └── main.cpp ├── install.sh ├── install_files └── dkms │ └── dkms.conf ├── media ├── Conversion_Error.png ├── Division_-Normal,_128Bit,_Big-.png ├── Division_-Precise,_128Bit,_Big-.png ├── Division_-Precise,_128Bit,_Small-.png ├── Division_-Precise,_64Bit,_Big-.png ├── Division_-Precise,_64Bit,_Small-.png ├── Exponent_-Fast,_Big-.png ├── Exponent_-Fast,_Small-.png ├── Exponent_-Precise,_Big-.png ├── Exponent_-Precise,_Small-.png ├── Exponent_Comparison_-Big-.png ├── Exponent_Comparison_-Small-.png ├── InstructionPerformance.png ├── Multiplication_-128Bit,_Big-.png ├── Multiplication_-128Bit,_Small-.png ├── Natural_Logarithm_-Fast,_Big-.png ├── Natural_Logarithm_-Fast,_Small-.png ├── Natural_Logarithm_-Precise,_Big-.png ├── Natural_Logarithm_-Precise,_Small-.png ├── Natural_Logarithm_Comparison_-Big-.png ├── Natural_Logarithm_Comparison_-Small-.png ├── Power_-Fast,_Big-.png ├── Power_-Fast,_Small-.png ├── Power_-Precise,_Big-.png ├── Power_-Precise,_Small-.png ├── Square_Root_-Fast,_Big-.png ├── Square_Root_-Fast,_Small-.png ├── Square_Root_-Precise,_Big-.png ├── Square_Root_-Precise,_Small-.png ├── Square_Root_Comparison_-Big-.png ├── Square_Root_Comparison_-Small-.png ├── YeetMouseExportSaveConfig.png └── YeetMouseGithub.gif ├── nix ├── README.md ├── flake.lock ├── flake.nix ├── module.nix └── package.nix ├── pkg ├── PKGBUILD.template └── yeetmouse-driver-dkms.install ├── scripts ├── bind.sh ├── build_arch.sh └── build_pkg.sh ├── shared_definitions.h ├── tests ├── CMakeLists.txt ├── CleanUpFiles.py ├── Readme.md ├── TestManager.cpp ├── TestManager.h ├── Tests.cpp ├── Tests.h ├── driver │ └── config.h └── main.cpp └── uninstall.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *.cmd 2 | *.ko 3 | *.mod 4 | *.mod.c 5 | *.mod.o 6 | *.o 7 | *.kate-swp 8 | *.out 9 | .directory 10 | Module.symvers 11 | modules.order 12 | .vscode 13 | config.h 14 | pkg/build 15 | .directory 16 | __pycache__ 17 | -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | patreon: AndyFilter 4 | custom: ['https://www.paypal.com/donate/?hosted_button_id=A5DYXBHYKBGAU'] -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile structure blatantly "stolen" from OpenRazer 3.0.0 and adapted for leetmouse 2 | SHELL := /bin/bash 3 | 4 | # DESTDIR is used to install into a different root directory 5 | DESTDIR?=/ 6 | # Specify the kernel directory to use 7 | KERNELDIR?=/lib/modules/$(shell uname -r)/build 8 | # Need the absolute directory do the driver directory to build kernel modules 9 | DRIVERDIR?=$(shell pwd)/driver 10 | 11 | GUIDIR?=$(shell pwd)/gui 12 | 13 | # Where kernel drivers are going to be installed 14 | MODULEDIR?=/lib/modules/$(shell uname -r)/kernel/drivers/usb 15 | 16 | DKMS_NAME?=yeetmouse-driver 17 | DKMS_VER?=0.9.2 18 | 19 | # Detect architecture 20 | ARCH := $(shell uname -m) 21 | 22 | .PHONY: driver 23 | .PHONY: GUI 24 | 25 | default: GUI 26 | 27 | all: driver 28 | clean: driver_clean 29 | 30 | GUI: 31 | @echo -e "\n::\033[32m Building GUI application\033[0m" 32 | @echo "========================================" 33 | #cd $(GUIDIR) && $(MAKE) 34 | $(MAKE) -C $(GUIDIR) M=$(GUIDIR) 35 | @echo "DONE!" 36 | 37 | driver: 38 | @echo -e "\n::\033[32m Compiling yeetmouse kernel module\033[0m" 39 | @echo "========================================" 40 | ifeq ($(ARCH),ppc64le) 41 | @echo "PowerPC 64-bit Little Endian detected" 42 | endif 43 | @cp -n $(DRIVERDIR)/config.sample.h $(DRIVERDIR)/config.h || true 44 | $(MAKE) -C $(KERNELDIR) M=$(DRIVERDIR) modules 45 | 46 | 47 | driver_clean: 48 | @echo -e "\n::\033[32m Cleaning yeetmouse kernel module\033[0m" 49 | @echo "========================================" 50 | $(MAKE) -C "$(KERNELDIR)" M="$(DRIVERDIR)" clean 51 | 52 | # Install kernel modules and then update module dependencies 53 | driver_install: 54 | @echo -e "\n::\033[34m Installing yeetmouse kernel module\033[0m" 55 | @echo "=====================================================" 56 | @mkdir -p $(DESTDIR)/$(MODULEDIR) 57 | @cp -v $(DRIVERDIR)/yeetmouse.ko $(DESTDIR)/$(MODULEDIR) 58 | @chown -v root:root $(DESTDIR)/$(MODULEDIR)/yeetmouse.ko 59 | depmod 60 | 61 | # Remove kernel modules 62 | driver_uninstall: 63 | @echo -e "\n::\033[34m Uninstalling yeetmouse kernel module\033[0m" 64 | @echo "=====================================================" 65 | @rm -fv $(DESTDIR)/$(MODULEDIR)/yeetmouse.ko 66 | 67 | setup_dkms: 68 | @echo -e "\n::\033[34m Installing DKMS files\033[0m" 69 | @echo "=====================================================" 70 | install -m 644 -v -D Makefile $(DESTDIR)/usr/src/$(DKMS_NAME)-$(DKMS_VER)/Makefile 71 | install -m 644 -v -D install_files/dkms/dkms.conf $(DESTDIR)/usr/src/$(DKMS_NAME)-$(DKMS_VER)/dkms.conf 72 | install -m 755 -v -d driver $(DESTDIR)/usr/src/$(DKMS_NAME)-$(DKMS_VER)/driver 73 | install -m 755 -v -d driver/FixedMath $(DESTDIR)/usr/src/$(DKMS_NAME)-$(DKMS_VER)/driver/FixedMath 74 | install -m 644 -v -D driver/Makefile $(DESTDIR)/usr/src/$(DKMS_NAME)-$(DKMS_VER)/driver/Makefile 75 | install -m 644 -v driver/*.c $(DESTDIR)/usr/src/$(DKMS_NAME)-$(DKMS_VER)/driver/ 76 | install -m 644 -v driver/*.h $(DESTDIR)/usr/src/$(DKMS_NAME)-$(DKMS_VER)/driver/ 77 | install -m 644 -v driver/FixedMath/*.h $(DESTDIR)/usr/src/$(DKMS_NAME)-$(DKMS_VER)/driver/FixedMath/ 78 | install -m 644 -v shared_definitions.h $(DESTDIR)/usr/src/$(DKMS_NAME)-$(DKMS_VER)/ 79 | $(if $(shell grep "AccelMode_" "$(DRIVERDIR)/config.h"),,@echo "\033[31mWARNING! Old config version detected, acceleration mode might be wrong!\033[0m") 80 | @rm -fv $(DESTDIR)/usr/src/$(DKMS_NAME)-$(DKMS_VER)/driver/*.mod.c 81 | 82 | remove_dkms: 83 | @echo -e "\n::\033[34m Removing DKMS files\033[0m" 84 | @echo "=====================================================" 85 | @rm -rf $(DESTDIR)/usr/src/$(DKMS_NAME)-$(DKMS_VER) 86 | 87 | install_i_know_what_i_am_doing: all driver_install 88 | install: manual_install_msg ; 89 | 90 | package: 91 | @echo -e "\n::\033[34m Building installable package\033[0m" 92 | @echo "=====================================================" 93 | @./scripts/build_arch.sh 94 | @mv ./pkg/build/yeetmouse*.zst . 95 | 96 | manual_install_msg: 97 | @echo "Please do not install the driver using this method. Use a distribution package as it tracks the files installed and can remove them afterwards. If you are 100% sure, you want to do this, find the correct target in the Makefile." 98 | @echo "Exiting." 99 | 100 | uninstall: driver_uninstall 101 | -------------------------------------------------------------------------------- /Performance.md: -------------------------------------------------------------------------------- 1 | # Contents 2 | 3 | * [Why even use Fixed-Point arithmetic?](#why-even-use-fixed-point-arithmetic) 4 | * [Benchmarking Methodology](#benchmarking-methodology) 5 | * [Testing Setup](#testing-setup) 6 | * [Performance](#performance) 7 | * [Precision](#precision) 8 | * [Use of 128-bit wide data types](#use-of-128-bit-wide-data-types) 9 | * [Default FP64_DivPrecise](#default-fp64_divprecise) 10 | * [128bit Enabled FP64_DivPrecise](#128bit-enabled-fp64_divprecise) 11 | * [Default FP64_Mul](#default-fp64_mul) 12 | * [128bit Enabled FP64_Mul](#128bit-enabled-fp64_mul) 13 | * [Precision](#precision-1) 14 | * [Multiplication](#multiplication) 15 | * [Division](#division) 16 | * [Exponent](#exponent) 17 | * [Square Root](#square-root) 18 | * [Pow](#pow) 19 | * [Log](#log) 20 | * [Real-Life Performance Gains](#real-life-performance-gains) 21 | 22 | 23 | # Why even use Fixed-Point arithmetic? 24 | It might sound pointless and even a bit silly if you've never tried writing kernel-level code that uses the FPU (or floating-point numbers in general). 25 | However, let's just say it can cause a lot of issues. Whenever you're performing some calculation, it might fail unexpectedly. 26 | Even if you've checked for the correct IRQ and aren't touching the client-side code, your numbers might just go bad. 27 | If that happens, the best you can do is say, "Oh well... Let's just try it again." 28 | As you might imagine, that's not something I want to deal with in a mouse driver that's meant to be fast and responsive. 29 | 30 | The second issue is precision. It's really not that great when you start dealing with things like exponents, 31 | where you might have a huge resulting number, but the fractional part could also be important for future calculations. 32 | This isn't a big issue for this application, as floating-point usually provides more than enough precision for the differences to be unnoticeable. 33 | 34 | The last concern is speed. This might not play a huge role since we don't do a lot of calculations, 35 | but we do perform them quite frequently (usually around 1,000 times a second). While the FPU is specifically optimized for complex floating-point operations and excels at them, 36 | it's not as fast for simple tasks like addition. Fixed-point numbers require a simple add instruction, whereas with the FPU, 37 | you need to call an actual function that handles the addition. 38 | That's not too bad, but remember that you’re not computing acosh too often, right? On the other hand, addition and subtraction are used pretty frequently. 39 | I still don't think the difference of a couple of instructions is enough to bridge the gap between more complex operations, but at the end of the day, 40 | performance should be comparable. 41 | 42 | # Benchmarking Methodology 43 | ### Testing Setup 44 | All tests were run on *Ubuntu 24.04 LTS*, CPU: *AMD Ryzen 5900X* 3.7 GHz on a single thread in *Power Saver* mode (I have no idea what *Power Saver* mode does, I just have it on). 45 | Code was compiled with the following flags: `-Wall -O2 -lgcc -mhard-float -lm -lc` (I used `-O2`, because that's what is used in the kernel) 46 | For Performance tests, final result was an average of 10 loops, with 100ms delay between them, each for 100'000 iterations. 47 | Keep in mind that these tests were **not** meant to determine (for example) the number of CPU instructions each function takes, 48 | but rather to compare the relative performance between functions that are meant to do the same. 49 | 50 | ### Performance 51 | Measuring for performance such simple functions is not an easy task, but thankfully I was mostly interested in their relative performance. 52 | This allowed me to just check the assembly every time I compiled the benchmarking code to make sure the function is actually run, 53 | instead of for example multiplying some input times 100000 (number of loops) and returning it. These scenarios are easy to spot tho, 54 | because I can instantly see that the instruction averaged for example 0.0002 ops/cycle, which is *unlikely* to put it lightly. 55 | On the other hand, there were more complex instructions that have checks like: 56 | ```c 57 | static FP_LONG FP64_Exp2(FP_LONG x) { 58 | // Handle values that would under or overflow. 59 | if (x >= 32 * One) return MaxValue; 60 | if (x <= -32 * One) return 0; 61 | (...) 62 | ``` 63 | Taking care of such edge cases is not that easy, because if not dealt with properly, the function would simply return instantly, and the measurement would be useless. 64 | So I had to add stuff like this `v2 = FP64_ExpFast(v2) >> 30;` to avoid overflowing and early returning. This just `bitshifts` to the right by 30 places, meaning that we get at most 2 bits of integer values, 65 | and 30 bits of fractional part are discarded. 66 | Analyzing the assembly doesn't show this at all. 67 | The only way to detect this is by running tests, looking at the results and going like *huh... that can't be right.* And so I did. 68 | 69 | ### Precision 70 | All the tests were done with comparison to the results of double arithmetics, because both are 64-Bit long. 71 | *Example code:* 72 | ```c++ 73 | f1 = log(point); 74 | v1 = FP64_LogFast(FP64_FromDouble(point)); 75 | f2 = FP64_ToDouble(v1); 76 | printf("%.12lf", fabs(f1-f2)); // Print to the file 77 | ``` 78 | 79 | The 2D tests were done in a similar fashion, just with 2 arguments for each of the functions. 80 | 81 | The range values for each test were chosen arbitrarily. Small one were meant to represent values up to about a hundred, while the big ones were usually up to the numerical limits of the Fixed-Point type. 82 | 83 | # Use of 128-bit wide data types 84 | #### *These are available on 64bit systems only* 85 | 86 | ## Default FP64_DivPrecise 87 | > (*Way too much asm to fit in here, that's also why it's so slow.*) 88 | 89 | | Mop/s | ns/op | Clock cycles/op | 90 | |:----------:|:----------:|:---------------:| 91 | | 88.169046 | 11.402000 | 42.139448 | 92 | 93 | 94 | ## 128bit Enabled FP64_DivPrecise 95 | 96 | ```asm 97 | .L4: 98 | movq %r14, %rax 99 | movq %r13, %rcx 100 | cqto 101 | salq $32, %rax 102 | shldq $32, %r14, %rdx 103 | movq %rax, %rdi 104 | movq %rdx, %rsi 105 | movq %r12, %rdx 106 | call __divti3@PLT 107 | movq %rax, %r15 108 | movq %rax, %r14 109 | subl $1, %ebx 110 | jne .L4 111 | ``` 112 | This simply uses `cqto` to enable octoword. It's still not the fastest, but it's about just as fast as a hardware FPU working with doubles. 113 | > `cqto:` *Convert quadword in %rax to octoword in %rdx:%rax.* 114 | 115 | | Mop/s | ns/op | Clock cycles/op | 116 | |:----------:|:---------:|:---------------:| 117 | | 168.716370 | 5.956000 | 21.957539 | 118 | 119 | ## Default FP64_Mul 120 | ```asm 121 | .L4: 122 | movl %r14d, %eax 123 | sarq $32, %r14 124 | movq %rax, %rdx 125 | imulq %r15, %r14 126 | imulq %r8, %rdx 127 | imulq %r9, %rax 128 | shrq $32, %rdx 129 | addq %rdx, %r14 130 | addq %rax, %r14 131 | subl $1, %edi 132 | jne .L4 133 | ``` 134 | 135 | | Mop/s | ns/op | Clock cycles/op | 136 | |:----------:|:--------:|:---------------:| 137 | | 394.653702 | 2.585000 | 9.514920 | 138 | 139 | 140 | ## 128bit Enabled FP64_Mul 141 | ```asm 142 | .L4: 143 | movq %r14, %rax 144 | imulq %r13 145 | shrq $32, %rax 146 | movslq %eax, %r14 147 | movq %r14, %r15 148 | subl $1, %r8d 149 | jne .L4 150 | ``` 151 | 152 | What's a bit surprising here is that there is no call to `cqto` for example. Unlike with `FP64_Div()`. 153 | > `cqto:` *Convert quadword in %rax to octoword in %rdx:%rax.* 154 | 155 | In the end it does the same thing as it's 64bit counterpart, just with fewer instructions. 156 | 157 | | Mop/s | ns/op | Clock cycles/op | 158 | |:----------:|:--------:|:---------------:| 159 | | 460.711727 | 2.223000 | 8.158278 | 160 | 161 | 162 | 163 | # Precision 164 | 165 | Let's start off with a reference. Here's a conversion error graph: 166 | ![Conversion Error](media/Conversion_Error.png) 167 | This just shows the minimum error of the benchmarking proces caused by type conversion (and the floating point precision itself). 168 | As you can see it's about 10⁻¹⁰, which is very little. 169 | 170 | ### Multiplication 171 | Here, results for 128-Bit and 64-Bit are exactly the same. Look [128-Bit](#128bit-enabled-fp64_mul) and [64-Bit](#default-fp64_mul) to see the difference (or lack of) in instructions used 172 | 173 | ![Multiplication -128Bit, Small-.png](media/Multiplication_-128Bit,_Small-.png) 174 | > Log values scale 175 | 176 | ![Multiplication -128Bit, Big-.png](media/Multiplication_-128Bit,_Big-.png) 177 | **White on this graph represents 0, and yellow parts are simply caused by an overflow (>2³¹)* 178 | 179 | To no one's surprise error here is negligible. 180 | 181 | ### Division 182 | > Log values scale 183 | 184 | ![Division_-Precise,_64Bit,_Small-.png](media/Division_-Precise,_64Bit,_Small-.png) 185 | ![Division_-Precise,_128Bit,_Small-.png](media/Division_-Precise,_128Bit,_Small-.png) 186 | ![Division_-Precise,_64Bit,_Small-.png](media/Division_-Precise,_64Bit,_Big-.png) 187 | ![Division_-Precise,_128Bit,_Small-.png](media/Division_-Precise,_128Bit,_Big-.png) 188 | 189 | Again, the difference in precision is non-existent. That's why it's strongly advised to use the 128-Bit data types. 190 | 191 | Just for fun I decided to see the default FP64_Div (not precise) version on big scale: 192 | ![Division_-Normal,_128Bit,_Small-.png](media/Division_-Normal,_128Bit,_Big-.png) 193 | 194 | The error here is huge compared to the precise version. And it looks way nicer. 195 | 196 | ### Exponent 197 | > Log X and Y scale 198 | 199 | Exp starts returning gibberish at about ln(2^31) (~21.487), as this is the integer limit for the 64bit Fixed point (Q32.32). 200 | 201 | ![Exponent_Comparison_-Small-.png](media/Exponent_Comparison_-Small-.png) 202 | ![Exponent_Comparison_-Big-.png](media/Exponent_Comparison_-Big-.png) 203 | 204 | I think that for smaller values it's sensible to use the `ExpFast`, while it might be more beneficial to use the `ExpPrecise` when higher level of precision is required. 205 | 206 | ### Square Root 207 | > Log Y scale 208 | 209 | ![Sqrt_Comparison_-Small-.png](media/Square_Root_Comparison_-Small-.png) 210 | ![Sqrt_Comparison_-Big-.png](media/Square_Root_Comparison_-Big-.png) 211 | 212 | Here we can again see the wavy pattern, but the overall precision of the Fast version is pretty good. 213 | I'd even say it's just enough for what's required, as we only deal with rather small input values of sqrt, where the precision is even better. 214 | This function is called every `irq`, so keeping it fast is crucial. This amount of precision is acceptable for our purpose. 215 | 216 | ### Pow 217 | 218 | **Small** 219 | 220 | > Log Values scale 221 | 222 | ![Power_-Precise,_Small-.png](media/Power_-Precise,_Small-.png) 223 | ![Power_-Fast,_Small-.png](media/Power_-Fast,_Small-.png) 224 | But as you can see. The absolute error for relatively small values is also relatively small, no matter the version (`Fast` or `Precise`). 225 | 226 | **Big** 227 | 228 | > Log Values scale 229 | 230 | ![Power_-Precise,_Big-.png](media/Power_-Precise,_Big-.png) 231 | ![Power_-Fast,_Big-.png](media/Power_-Fast,_Big-.png) 232 | 233 | For this test (like for many others) it would be nice to have a relative error, but I decided to keep everything the same, so not to introduce any unnecessary confusion. 234 | I think it is pretty safe to say that the precision of the `Fast` version is enough. 235 | 236 | ### Log 237 | > Log Y scale 238 | 239 | ![Natural_Logarithm_Comparison_-Small-.png](media/Natural_Logarithm_Comparison_-Small-.png) 240 | ![Natural_Logarithm_Comparison_-Big-.png](media/Natural_Logarithm_Comparison_-Big-.png) 241 | 242 | The precision of both `Fast`, and `Precise` functions is very good. Thus, I will stick with the `Fast` version. 243 | 244 | 245 | # Real-Life Performance Gains 246 | All the above comes down to this: 247 | 248 | | | Fixed Precise | Floating (float) | Fixed Fast* | 249 | |:----------------------:|:-------------:|:----------------:|:-----------:| 250 | | **Avg. IRQ time [ns]** | 346.7052 | 343.2419 | 190.4042 | 251 | 252 | *(The tests were conducted using `Jump` with smoothing on, as it's the most calculation heavy setting)* 253 | 254 | I'm quite happy with this performance, as the `Precise` version is surely much more precise than `float`, and the `Fast` version 255 | is precise enough not to notice a difference while giving a significant performance boost (*about 1.5x in most cases*). 256 | By default the program will now run at the `Fixed Fast` setting. 257 | 258 | **"Fixed Fast" is one level faster than the most precise version, so if a function has a `Precise` version like `FP64_SqrtPrecise()`, then 259 | `FP64_Sqrt()` is used as the `Fast` version. It also implements all the optimizations I managed to come up with.* 260 | 261 | 262 | *If you were to only look at the images, this page would look like a failed modern art project...* -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | * Description 2 | *YeetMouse* is a kernel module that allows for fast and customizable mouse acceleration. 3 | I created *YeetMouse* to build upon the great work of /Klaus Zipfel's/ *[[https://github.com/systemofapwne/leetmouse][LeetMouse]]* and add features such as =Jump= (at first it was mostly it, the Jump acceleration mode). 4 | But it quickly grew into something bigger as I decided to add a GUI to it for better control. 5 | 6 | #+CAPTION: Basic functions presentation 7 | [[media/YeetMouseGithub.gif]] 8 | 9 | * Installation & Uninstallation 10 | ** Preparation & Configuration 11 | + Clone this repository, find the settings you're most comfortable with, using GUI, change the defines in the config.h (More on this in the [[https://github.com/AndyFilter/YeetMouse?tab=readme-ov-file#settings-are-not-preserved-between-reboots][FAQ]] section about persistent settings). 12 | + Make sure, you have the required toolchains (e.g. =base-devel= on Arch or =build-essentials= on Debian) installed as well as the =linux-headers= for your installed kernel. 13 | 14 | ** Running the GUI 15 | *** Prerequisites: 16 | + OpenGL (you most likely already have it) 17 | + GLFW3, which can be installed via these commands: 18 | #+begin_src sh 19 | sudo apt-get install libglfw3 20 | sudo apt-get install libglfw3-dev 21 | #+end_src 22 | 23 | *** Building 24 | To build, go to the gui directory =cd gui/= and run =make= 25 | 26 | *** Starting 27 | Keep in mind that the program needs to be *run with sudo privileges*. 28 | To run, simply use =sudo -E ./YeetMouseGui= 29 | 30 | ** Arch/Manjaro 31 | For Arch and Manjaro, a =PKGBUILD= has been written for seamless integration into pacman. 32 | 33 | *Installation* 34 | #+begin_src sh 35 | # Create the PKGBUILD, copy associated files and run makepkg 36 | ./scripts/build_arch.sh 37 | # Run pacman on the created package 38 | sudo pacman -U pkg/build/yeetmouse*.zst 39 | #+end_src 40 | All your mice should now be bound to this driver. They will also automatically bind to it after a reboot. 41 | 42 | *Uninstallation* 43 | #+begin_src sh 44 | sudo pacman -R yeetmouse-driver-dkms 45 | #+end_src 46 | All your mice should now be bound to the generic usbhid driver again. 47 | 48 | ** Ubuntu (tested on 24.04 and 20.04) 49 | Simply use the installation and uninstallation scripts, =sudo ./install.sh= and =sudo ./uninstall.sh= respectively. 50 | If something doesn't work as expected, try installing manually as stated in section *Other distros*, and check all the error logs. 51 | 52 | ** NixOS 53 | Please refer to [[nix/][NixOS instructions]]. 54 | 55 | ** Other distros 56 | Other distributions' package-managers are not yet supported and thus need a manual installation. 57 | 58 | *Installation* 59 | 60 | Determine the current version of this module by examining the variable =DKMS_VER= in the =Makefile=. It can be e.g. 0.9.2 61 | Run the following commands to build and install the kernel module 62 | #+begin_src sh 63 | # Install the driver and activate the dkms module 64 | sudo make setup_dkms 65 | sudo dkms install -m yeetmouse-driver -v 0.9.2 # Enter the version you determined from the Makefile earlier in here 66 | sudo modprobe yeetmouse 67 | #+end_src 68 | (Or you can use the built-in install script like so: =sudo ./install.sh=). 69 | 70 | All your mice should now be bound to this driver. They will also automatically bind to it after a reboot. 71 | If this still does not work, there is a major problem 72 | 73 | *Uninstallation* 74 | 75 | You again need to know the =version= as described above for the installation 76 | #+begin_src sh 77 | # Uninstall the driver 78 | sudo dkms remove yeetmouse-driver/0.9.2 --all 79 | sudo make remove_dkms 80 | sudo rmmod yeetmouse 81 | #+end_src 82 | (Or you can use the built-in uninstall script like so: =sudo ./uninstall.sh=, this script determines installed driver version automatically). 83 | 84 | Unplug and replug your mouse or reboot to have your mice bound back to the generic usbhid. 85 | * Manual compile, insmod, bind 86 | If you want to compile this module only for testing purposes or development, you do not need to install the whole package to your system 87 | 88 | Compile the module, remove previously loaded modules and insert it. 89 | #+begin_src sh 90 | make clean && make 91 | sudo rmmod yeetmouse 92 | sudo insmod ./driver/yeetmouse.ko 93 | #+end_src 94 | 95 | * FAQ 96 | *** How to set custom parameter value? 97 | - Ctrl + Left Click on the parameter box to start inputting the values manually. 98 | 99 | 100 | *** Settings are not preserved between reboots 101 | - This is (unfortunately) how things are done on most distros, for security reasons I presume. 102 | But there is a way so save the settings. All You need to do is find the values You like and Export them to a config.h format like so: 103 | #+CAPTION: Exporting config to a .h format 104 | [[media/YeetMouseExportSaveConfig.png]] 105 | 106 | Then simply replace the =config.h= file located in =/driver= (or create a one), and reinstall the driver (uninstall and install). 107 | 108 | *** Mouse feels off (too fast / slow) 109 | - On some distros (for example Ubuntu 20.04) system adds an additional sensitivity on top of the driver. To combat this You'll need to configure the settings correctly. 110 | This is system dependant, but for Ubuntu 20.04 users, the exact sensitivity value is -0.666, to apply that, simply use =gsettings set org.gnome.desktop.peripherals.mouse speed -0.666= 111 | 112 | 113 | *** How do I convert my RawAccel settings? 114 | - For the simple modes like /Linear, Classic, Power/ just use the RawAccel's values (same for /Jump/). 115 | - For /Motivity/ and /Natural/, You're out of luck for now. Motivity is implemented, but it does not support =Gain=. Natural on the other hand is not implemented, and not planned as of for now. 116 | - LuT (Look up Table) is just what you put in it, there is no difference between YeetMouse and RawAccel. 117 | - Keep in mind that the names are not 1:1 for every parameter. 118 | - To check how Your new curve compares to RawAccel's, just take a screenshot of RawAccel with your curve and compare the two. 119 | 120 | 121 | * Fixed-Point Performance Analysis 122 | #+CAPTION: Functions Performance Comparison 123 | [[media/InstructionPerformance.png]] 124 | 125 | #+BEGIN_HTML 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 |
InstructionFixed-Point / FPUMop/sns/opClock cycles/op
MultiplicationFixed-Point 64542.9053671.9117.029038
Fixed-Point 64 (128bit)540.6826951.9137.012462
FPU (double)788.5241051.294.722532
DivisionFixed-Point 64 (Precise)91.44641911.29941.756461
Fixed-Point 64 (128bit)203.8191515.09718.797924
FPU (double)188.0357045.39219.879064
ExponentFixed-Point 6466.55084515.56157.525454
Fixed-Point 64 (Fast)92.77536611.28541.702182
FPU (double)116.3964438.74132.276506
SqrtFixed-Point 64 (Precise)18.05989557.307211.97892
Fixed-Point 6464.55879215.67557.956097
FPU (double)133.4745347.929.179384
PowFixed-Point 6431.8129432.221119.111214
Fixed-Point 64 (Fast)40.52452726.04396.310556
FPU (double)77.80454417.11363.251944
LogFixed-Point 6451.11707321.03377.768302
Fixed-Point 64 (Fast)61.34195116.63861.497848
FPU (double)53.32606519.87673.491065
250 | #+END_HTML 251 | 252 | **** /More in-depth performance and precision analysis can be found [[Performance.md][here]]/. -------------------------------------------------------------------------------- /debug/devices/coolermaster_mm710_descriptor.txt: -------------------------------------------------------------------------------- 1 | 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 2 | 0x09, 0x02, // Usage (Mouse) 3 | 0xA1, 0x01, // Collection (Application) 4 | 0x09, 0x01, // Usage (Pointer) 5 | 0xA1, 0x00, // Collection (Physical) 6 | 7 | 0x05, 0x09, // Usage Page (Button) 8 | 0x19, 0x01, // Usage Minimum (0x01) 9 | 0x29, 0x10, // Usage Maximum (0x10) 10 | 0x15, 0x00, // Logical Minimum (0) 11 | 0x25, 0x01, // Logical Maximum (1) 12 | 0x75, 0x01, // Report Size (1) 13 | 0x95, 0x10, // Report Count (16) 14 | 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 15 | 16 | 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 17 | 0x09, 0x30, // Usage (X) 18 | 0x09, 0x31, // Usage (Y) 19 | 0x16, 0x01, 0x80, // Logical Minimum (-32767) 20 | 0x26, 0xFF, 0x7F, // Logical Maximum (32767) 21 | 0x75, 0x10, // Report Size (16) 22 | 0x95, 0x02, // Report Count (2) 23 | 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) 24 | 25 | 0x09, 0x38, // Usage (Wheel) 26 | 0x15, 0x81, // Logical Minimum (-127) 27 | 0x25, 0x7F, // Logical Maximum (127) 28 | 0x75, 0x08, // Report Size (8) 29 | 0x95, 0x01, // Report Count (1) 30 | 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) 31 | 32 | 0x05, 0x0C, // Usage Page (Consumer) 33 | 0x0A, 0x38, 0x02, // Usage (AC Pan) 34 | 0x95, 0x01, // Report Count (1) 35 | 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) 36 | 0xC0, // End Collection 37 | 0xC0, // End Collection 38 | 39 | // 67 bytes 40 | -------------------------------------------------------------------------------- /debug/devices/coolermaster_mm710_descriptor_raw.txt: -------------------------------------------------------------------------------- 1 | 001:017:002:DESCRIPTOR 1617602495.479309 2 | 05 01 09 80 A1 01 85 01 19 81 29 83 15 00 25 01 3 | 95 03 75 01 81 02 95 01 75 05 81 01 C0 05 0C 09 4 | 01 A1 01 85 02 15 00 25 01 95 12 75 01 0A 83 01 5 | 0A 8A 01 0A 92 01 0A 94 01 09 CD 09 B7 09 B6 09 6 | B5 09 E2 09 EA 09 E9 0A 21 02 0A 23 02 0A 24 02 7 | 0A 25 02 0A 26 02 0A 27 02 0A 2A 02 81 02 95 01 8 | 75 0E 81 01 C0 05 01 09 06 A1 01 85 04 05 07 95 9 | 01 75 08 81 03 95 E8 75 01 15 00 25 01 05 07 19 10 | 00 29 E7 81 00 C0 11 | 12 | 001:017:001:DESCRIPTOR 1617602495.479733 13 | 06 00 FF 09 01 A1 01 09 02 15 00 26 FF 00 75 08 14 | 95 40 81 02 09 03 15 00 26 FF 00 75 08 95 40 91 15 | 02 C0 16 | 17 | 001:017:000:DESCRIPTOR 1617602495.480086 18 | 05 01 09 02 A1 01 09 01 A1 00 05 09 19 01 29 10 19 | 15 00 25 01 75 01 95 10 81 02 05 01 09 30 09 31 20 | 16 01 80 26 FF 7F 75 10 95 02 81 06 09 38 15 81 21 | 25 7F 75 08 95 01 81 06 05 0C 0A 38 02 95 01 81 22 | 06 C0 C0 23 | -------------------------------------------------------------------------------- /debug/devices/csl_optical_mouse_descriptor.txt: -------------------------------------------------------------------------------- 1 | 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 2 | 0x09, 0x02, // Usage (Mouse) 3 | 0xA1, 0x01, // Collection (Application) 4 | 0x85, 0x01, // Report ID (1) 5 | 0x09, 0x01, // Usage (Pointer) 6 | 0xA1, 0x00, // Collection (Physical) 7 | 0x05, 0x09, // Usage Page (Button) 8 | 0x19, 0x01, // Usage Minimum (0x01) 9 | 0x29, 0x05, // Usage Maximum (0x05) 10 | 0x15, 0x00, // Logical Minimum (0) 11 | 0x25, 0x01, // Logical Maximum (1) 12 | 0x95, 0x05, // Report Count (5) 13 | 0x75, 0x01, // Report Size (1) 14 | 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 15 | 16 | 0x95, 0x01, // Report Count (1) 17 | 0x75, 0x03, // Report Size (3) 18 | 0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 19 | 20 | 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 21 | 0x16, 0x01, 0xF8, // Logical Minimum (-2047) 22 | 0x26, 0xFF, 0x07, // Logical Maximum (2047) 23 | 0x75, 0x0C, // Report Size (12) 24 | 0x95, 0x02, // Report Count (2) 25 | 0x09, 0x30, // Usage (X) 26 | 0x09, 0x31, // Usage (Y) 27 | 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) 28 | 29 | 0x15, 0x81, // Logical Minimum (-127) 30 | 0x25, 0x7F, // Logical Maximum (127) 31 | 0x75, 0x08, // Report Size (8) 32 | 0x95, 0x01, // Report Count (1) 33 | 0x09, 0x38, // Usage (Wheel) 34 | 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) 35 | 0xC0, // End Collection 36 | 0x05, 0x0C, // Usage Page (Consumer) 37 | 0x0A, 0x38, 0x02, // Usage (AC Pan) 38 | 0x95, 0x01, // Report Count (1) 39 | 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) 40 | 0xC0, // End Collection 41 | 42 | // 75 bytes 43 | 44 | -------------------------------------------------------------------------------- /debug/devices/csl_optical_mouse_descriptor_raw.txt: -------------------------------------------------------------------------------- 1 | 001:015:000:DESCRIPTOR 1618165844.728901 2 | 05 01 09 02 A1 01 85 01 09 01 A1 00 05 09 19 01 3 | 29 05 15 00 25 01 95 05 75 01 81 02 95 01 75 03 4 | 81 03 05 01 16 01 F8 26 FF 07 75 0C 95 02 09 30 5 | 09 31 81 06 15 81 25 7F 75 08 95 01 09 38 81 06 6 | C0 05 0C 0A 38 02 95 01 81 06 C0 7 | -------------------------------------------------------------------------------- /debug/devices/logitech_g5.txt: -------------------------------------------------------------------------------- 1 | Bus 005 Device 009: ID 046d:c049 Logitech, Inc. G5 Laser Mouse 2 | Device Descriptor: 3 | bLength 18 4 | bDescriptorType 1 5 | bcdUSB 2.00 6 | bDeviceClass 0 7 | bDeviceSubClass 0 8 | bDeviceProtocol 0 9 | bMaxPacketSize0 8 10 | idVendor 0x046d Logitech, Inc. 11 | idProduct 0xc049 G5 Laser Mouse 12 | bcdDevice 52.00 13 | iManufacturer 1 Logitech 14 | iProduct 2 USB Gaming Mouse 15 | iSerial 0 16 | bNumConfigurations 1 17 | Configuration Descriptor: 18 | bLength 9 19 | bDescriptorType 2 20 | wTotalLength 0x003b 21 | bNumInterfaces 2 22 | bConfigurationValue 1 23 | iConfiguration 4 24 | bmAttributes 0xa0 25 | (Bus Powered) 26 | Remote Wakeup 27 | MaxPower 98mA 28 | Interface Descriptor: 29 | bLength 9 30 | bDescriptorType 4 31 | bInterfaceNumber 0 32 | bAlternateSetting 0 33 | bNumEndpoints 1 34 | bInterfaceClass 3 Human Interface Device 35 | bInterfaceSubClass 1 Boot Interface Subclass 36 | bInterfaceProtocol 2 Mouse 37 | iInterface 0 38 | HID Device Descriptor: 39 | bLength 9 40 | bDescriptorType 33 41 | bcdHID 1.11 42 | bCountryCode 0 Not supported 43 | bNumDescriptors 1 44 | bDescriptorType 34 Report 45 | wDescriptorLength 94 46 | Report Descriptors: 47 | ** UNAVAILABLE ** 48 | Endpoint Descriptor: 49 | bLength 7 50 | bDescriptorType 5 51 | bEndpointAddress 0x81 EP 1 IN 52 | bmAttributes 3 53 | Transfer Type Interrupt 54 | Synch Type None 55 | Usage Type Data 56 | wMaxPacketSize 0x000a 1x 10 bytes 57 | bInterval 1 58 | Interface Descriptor: 59 | bLength 9 60 | bDescriptorType 4 61 | bInterfaceNumber 1 62 | bAlternateSetting 0 63 | bNumEndpoints 1 64 | bInterfaceClass 3 Human Interface Device 65 | bInterfaceSubClass 0 66 | bInterfaceProtocol 0 67 | iInterface 0 68 | HID Device Descriptor: 69 | bLength 9 70 | bDescriptorType 33 71 | bcdHID 1.11 72 | bCountryCode 0 Not supported 73 | bNumDescriptors 1 74 | bDescriptorType 34 Report 75 | wDescriptorLength 54 76 | Report Descriptors: 77 | ** UNAVAILABLE ** 78 | Endpoint Descriptor: 79 | bLength 7 80 | bDescriptorType 5 81 | bEndpointAddress 0x82 EP 2 IN 82 | bmAttributes 3 83 | Transfer Type Interrupt 84 | Synch Type None 85 | Usage Type Data 86 | wMaxPacketSize 0x0014 1x 20 bytes 87 | bInterval 10 88 | -------------------------------------------------------------------------------- /debug/devices/logitech_g5_descriptor.txt: -------------------------------------------------------------------------------- 1 | 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 2 | 0x09, 0x02, // Usage (Mouse) 3 | 0xA1, 0x01, // Collection (Application) 4 | 0x09, 0x01, // Usage (Pointer) 5 | 0xA1, 0x00, // Collection (Physical) 6 | 7 | 0x05, 0x09, // Usage Page (Button) 8 | 0x19, 0x01, // Usage Minimum (0x01) 9 | 0x29, 0x08, // Usage Maximum (0x08) 10 | 0x15, 0x00, // Logical Minimum (0) 11 | 0x25, 0x01, // Logical Maximum (1) 12 | 0x95, 0x08, // Report Count (8) 13 | 0x75, 0x01, // Report Size (1) 14 | 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 15 | 16 | 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) 17 | 0x09, 0x40, // Usage (0x40) 18 | 0x15, 0x81, // Logical Minimum (-127) 19 | 0x25, 0x7F, // Logical Maximum (127) 20 | 0x75, 0x08, // Report Size (8) 21 | 0x95, 0x02, // Report Count (2) 22 | 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 23 | 24 | 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 25 | 0x09, 0x38, // Usage (Wheel) 26 | 0x95, 0x01, // Report Count (1) 27 | 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) 28 | 29 | 0x05, 0x0C, // Usage Page (Consumer) 30 | 0x0A, 0x38, 0x02, // Usage (AC Pan) 31 | 0x95, 0x01, // Report Count (1) 32 | 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) 33 | 34 | 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 35 | 0x16, 0x01, 0x80, // Logical Minimum (-32767) 36 | 0x26, 0xFF, 0x7F, // Logical Maximum (32767) 37 | 0x75, 0x10, // Report Size (16) 38 | 0x95, 0x02, // Report Count (2) 39 | 0x09, 0x30, // Usage (X) 40 | 0x09, 0x31, // Usage (Y) 41 | 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) 42 | 43 | 0x05, 0x09, // Usage Page (Button) 44 | 0x19, 0x09, // Usage Minimum (0x09) 45 | 0x29, 0x10, // Usage Maximum (0x10) 46 | 0x15, 0x00, // Logical Minimum (0) 47 | 0x25, 0x01, // Logical Maximum (1) 48 | 0x95, 0x08, // Report Count (8) 49 | 0x75, 0x01, // Report Size (1) 50 | 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 51 | 0xC0, // End Collection 52 | 0xC0, // End Collection 53 | 54 | // 94 bytes 55 | -------------------------------------------------------------------------------- /debug/devices/logitech_g5_descriptor_raw.txt: -------------------------------------------------------------------------------- 1 | 005:009:001:DESCRIPTOR 1617489629.395783 2 | 06 00 FF 09 01 A1 01 85 10 75 08 95 06 15 00 26 3 | FF 00 09 01 81 00 09 01 91 00 C0 06 00 FF 09 02 4 | A1 01 85 11 75 08 95 13 15 00 26 FF 00 09 02 81 5 | 00 09 02 91 00 C0 6 | 7 | 005:009:000:DESCRIPTOR 1617489629.396795 8 | 05 01 09 02 A1 01 09 01 A1 00 05 09 19 01 29 08 9 | 15 00 25 01 95 08 75 01 81 02 06 00 FF 09 40 15 10 | 81 25 7F 75 08 95 02 81 02 05 01 09 38 95 01 81 11 | 06 05 0C 0A 38 02 95 01 81 06 05 01 16 01 80 26 12 | FF 7F 75 10 95 02 09 30 09 31 81 06 05 09 19 09 13 | 29 10 15 00 25 01 95 08 75 01 81 02 C0 C0 14 | -------------------------------------------------------------------------------- /debug/devices/steelseries_kana.txt: -------------------------------------------------------------------------------- 1 | Bus 001 Device 011: ID 1038:1364 SteelSeries ApS Kana Gaming Mouse 2 | Device Descriptor: 3 | bLength 18 4 | bDescriptorType 1 5 | bcdUSB 1.10 6 | bDeviceClass 0 7 | bDeviceSubClass 0 8 | bDeviceProtocol 0 9 | bMaxPacketSize0 64 10 | idVendor 0x1038 SteelSeries ApS 11 | idProduct 0x1364 12 | bcdDevice 1.07 13 | iManufacturer 1 SteelSeries 14 | iProduct 2 Kana Gaming Mouse 15 | iSerial 0 16 | bNumConfigurations 1 17 | Configuration Descriptor: 18 | bLength 9 19 | bDescriptorType 2 20 | wTotalLength 0x003b 21 | bNumInterfaces 2 22 | bConfigurationValue 1 23 | iConfiguration 0 24 | bmAttributes 0xa0 25 | (Bus Powered) 26 | Remote Wakeup 27 | MaxPower 100mA 28 | Interface Descriptor: 29 | bLength 9 30 | bDescriptorType 4 31 | bInterfaceNumber 0 32 | bAlternateSetting 0 33 | bNumEndpoints 1 34 | bInterfaceClass 3 Human Interface Device 35 | bInterfaceSubClass 0 36 | bInterfaceProtocol 0 37 | iInterface 0 38 | HID Device Descriptor: 39 | bLength 9 40 | bDescriptorType 33 41 | bcdHID 1.11 42 | bCountryCode 0 Not supported 43 | bNumDescriptors 1 44 | bDescriptorType 34 Report 45 | wDescriptorLength 37 46 | Report Descriptors: 47 | ** UNAVAILABLE ** 48 | Endpoint Descriptor: 49 | bLength 7 50 | bDescriptorType 5 51 | bEndpointAddress 0x81 EP 1 IN 52 | bmAttributes 3 53 | Transfer Type Interrupt 54 | Synch Type None 55 | Usage Type Data 56 | wMaxPacketSize 0x0020 1x 32 bytes 57 | bInterval 1 58 | Interface Descriptor: 59 | bLength 9 60 | bDescriptorType 4 61 | bInterfaceNumber 1 62 | bAlternateSetting 0 63 | bNumEndpoints 1 64 | bInterfaceClass 3 Human Interface Device 65 | bInterfaceSubClass 1 Boot Interface Subclass 66 | bInterfaceProtocol 2 Mouse 67 | iInterface 0 68 | HID Device Descriptor: 69 | bLength 9 70 | bDescriptorType 33 71 | bcdHID 1.11 72 | bCountryCode 0 Not supported 73 | bNumDescriptors 1 74 | bDescriptorType 34 Report 75 | wDescriptorLength 64 76 | Report Descriptors: 77 | ** UNAVAILABLE ** 78 | Endpoint Descriptor: 79 | bLength 7 80 | bDescriptorType 5 81 | bEndpointAddress 0x82 EP 2 IN 82 | bmAttributes 3 83 | Transfer Type Interrupt 84 | Synch Type None 85 | Usage Type Data 86 | wMaxPacketSize 0x0006 1x 6 bytes 87 | bInterval 1 88 | can't get debug descriptor: Resource temporarily unavailable 89 | Device Status: 0x0000 90 | (Bus Powered) 91 | -------------------------------------------------------------------------------- /debug/devices/steelseries_kana_descriptor.txt: -------------------------------------------------------------------------------- 1 | 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 2 | 0x09, 0x02, // Usage (Mouse) 3 | 0xA1, 0x01, // Collection (Application) 4 | 0x09, 0x01, // Usage (Pointer) 5 | 0xA1, 0x00, // Collection (Physical) 6 | 7 | 0x05, 0x09, // Usage Page (Button) 8 | 0x19, 0x01, // Usage Minimum (0x01) 9 | 0x29, 0x05, // Usage Maximum (0x05) 10 | 0x15, 0x00, // Logical Minimum (0) 11 | 0x25, 0x01, // Logical Maximum (1) 12 | 0x95, 0x05, // Report Count (5) 13 | 0x75, 0x01, // Report Size (1) 14 | 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 15 | 16 | 0x95, 0x03, // Report Count (3) 17 | 0x75, 0x01, // Report Size (1) 18 | 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) 19 | 20 | 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 21 | 0x09, 0x30, // Usage (X) 22 | 0x09, 0x31, // Usage (Y) 23 | 0x16, 0x01, 0x80, // Logical Minimum (-32767) 24 | 0x26, 0xFF, 0x7F, // Logical Maximum (32767) 25 | 0x75, 0x10, // Report Size (16) 26 | 0x95, 0x02, // Report Count (2) 27 | 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) 28 | 29 | 0x09, 0x38, // Usage (Wheel) 30 | 0x15, 0x81, // Logical Minimum (-127) 31 | 0x25, 0x7F, // Logical Maximum (127) 32 | 0x75, 0x08, // Report Size (8) 33 | 0x95, 0x01, // Report Count (1) 34 | 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) 35 | t 36 | 0xC0, // End Collection 37 | 0xC0, // End Collection 38 | 39 | // 64 bytes 40 | -------------------------------------------------------------------------------- /debug/devices/steelseries_kana_descriptor_raw.txt: -------------------------------------------------------------------------------- 1 | 001:011:001:DESCRIPTOR 1618007075.250715 2 | 05 01 09 02 A1 01 09 01 A1 00 05 09 19 01 29 05 3 | 15 00 25 01 95 05 75 01 81 02 95 03 75 01 81 01 4 | 05 01 09 30 09 31 16 01 80 26 FF 7F 75 10 95 02 5 | 81 06 09 38 15 81 25 7F 75 08 95 01 81 06 C0 C0 6 | 7 | 001:011:000:DESCRIPTOR 1618007075.251269 8 | 06 C0 FF 09 01 A1 01 06 C1 FF 15 00 26 FF 00 75 9 | 08 09 F0 95 20 81 02 09 F1 95 20 91 02 09 F2 96 10 | 02 02 B1 02 C0 11 | -------------------------------------------------------------------------------- /debug/devices/steelseries_rival600.txt: -------------------------------------------------------------------------------- 1 | Bus 005 Device 009: ID 1038:1724 SteelSeries ApS SteelSeries Rival 600 2 | Device Descriptor: 3 | bLength 18 4 | bDescriptorType 1 5 | bcdUSB 2.00 6 | bDeviceClass 0 7 | bDeviceSubClass 0 8 | bDeviceProtocol 0 9 | bMaxPacketSize0 64 10 | idVendor 0x1038 SteelSeries ApS 11 | idProduct 0x1724 12 | bcdDevice 2.34 13 | iManufacturer 1 SteelSeries 14 | iProduct 2 SteelSeries Rival 600 15 | iSerial 0 16 | bNumConfigurations 1 17 | Configuration Descriptor: 18 | bLength 9 19 | bDescriptorType 2 20 | wTotalLength 0x0054 21 | bNumInterfaces 3 22 | bConfigurationValue 1 23 | iConfiguration 0 24 | bmAttributes 0xa0 25 | (Bus Powered) 26 | Remote Wakeup 27 | MaxPower 200mA 28 | Interface Descriptor: 29 | bLength 9 30 | bDescriptorType 4 31 | bInterfaceNumber 0 32 | bAlternateSetting 0 33 | bNumEndpoints 1 34 | bInterfaceClass 3 Human Interface Device 35 | bInterfaceSubClass 0 36 | bInterfaceProtocol 0 37 | iInterface 0 38 | HID Device Descriptor: 39 | bLength 9 40 | bDescriptorType 33 41 | bcdHID 1.11 42 | bCountryCode 0 Not supported 43 | bNumDescriptors 1 44 | bDescriptorType 34 Report 45 | wDescriptorLength 37 46 | Report Descriptors: 47 | ** UNAVAILABLE ** 48 | Endpoint Descriptor: 49 | bLength 7 50 | bDescriptorType 5 51 | bEndpointAddress 0x81 EP 1 IN 52 | bmAttributes 3 53 | Transfer Type Interrupt 54 | Synch Type None 55 | Usage Type Data 56 | wMaxPacketSize 0x0020 1x 32 bytes 57 | bInterval 1 58 | Interface Descriptor: 59 | bLength 9 60 | bDescriptorType 4 61 | bInterfaceNumber 1 62 | bAlternateSetting 0 63 | bNumEndpoints 1 64 | bInterfaceClass 3 Human Interface Device 65 | bInterfaceSubClass 1 Boot Interface Subclass 66 | bInterfaceProtocol 2 Mouse 67 | iInterface 0 68 | HID Device Descriptor: 69 | bLength 9 70 | bDescriptorType 33 71 | bcdHID 1.11 72 | bCountryCode 0 Not supported 73 | bNumDescriptors 1 74 | bDescriptorType 34 Report 75 | wDescriptorLength 98 76 | Report Descriptors: 77 | ** UNAVAILABLE ** 78 | Endpoint Descriptor: 79 | bLength 7 80 | bDescriptorType 5 81 | bEndpointAddress 0x82 EP 2 IN 82 | bmAttributes 3 83 | Transfer Type Interrupt 84 | Synch Type None 85 | Usage Type Data 86 | wMaxPacketSize 0x0009 1x 9 bytes 87 | bInterval 1 88 | Interface Descriptor: 89 | bLength 9 90 | bDescriptorType 4 91 | bInterfaceNumber 2 92 | bAlternateSetting 0 93 | bNumEndpoints 1 94 | bInterfaceClass 3 Human Interface Device 95 | bInterfaceSubClass 0 96 | bInterfaceProtocol 0 97 | iInterface 0 98 | HID Device Descriptor: 99 | bLength 9 100 | bDescriptorType 33 101 | bcdHID 1.11 102 | bCountryCode 0 Not supported 103 | bNumDescriptors 1 104 | bDescriptorType 34 Report 105 | wDescriptorLength 76 106 | Report Descriptors: 107 | ** UNAVAILABLE ** 108 | Endpoint Descriptor: 109 | bLength 7 110 | bDescriptorType 5 111 | bEndpointAddress 0x83 EP 3 IN 112 | bmAttributes 3 113 | Transfer Type Interrupt 114 | Synch Type None 115 | Usage Type Data 116 | wMaxPacketSize 0x0009 1x 9 bytes 117 | bInterval 1 118 | -------------------------------------------------------------------------------- /debug/devices/steelseries_rival600_descriptor.txt: -------------------------------------------------------------------------------- 1 | 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 2 | 0x09, 0x02, // Usage (Mouse) 3 | 0xA1, 0x01, // Collection (Application) 4 | 0x09, 0x01, // Usage (Pointer) 5 | 0xA1, 0x00, // Collection (Physical) 6 | 0xA1, 0x02, // Collection (Logical) 7 | 8 | 0x05, 0x09, // Usage Page (Button) 9 | 0x19, 0x01, // Usage Minimum (0x01) 10 | 0x29, 0x08, // Usage Maximum (0x08) 11 | 0x15, 0x00, // Logical Minimum (0) 12 | 0x25, 0x01, // Logical Maximum (1) 13 | 0x95, 0x08, // Report Count (8) 14 | 0x75, 0x01, // Report Size (1) 15 | 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 16 | 17 | 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 18 | 0x09, 0x30, // Usage (X) 19 | 0x09, 0x31, // Usage (Y) 20 | 0x16, 0x01, 0x80, // Logical Minimum (-32767) 21 | 0x26, 0xFF, 0x7F, // Logical Maximum (32767) 22 | 0x75, 0x10, // Report Size (16) 23 | 0x95, 0x02, // Report Count (2) 24 | 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) 25 | 26 | 0x09, 0x38, // Usage (Wheel) 27 | 0x15, 0x81, // Logical Minimum (-127) 28 | 0x25, 0x7F, // Logical Maximum (127) 29 | 0x75, 0x08, // Report Size (8) 30 | 0x95, 0x01, // Report Count (1) 31 | 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) 32 | 33 | 0xC0, // End Collection 34 | 0xA1, 0x02, // Collection (Logical) 35 | 36 | 0x05, 0x0C, // Usage Page (Consumer) 37 | 0x0A, 0x38, 0x02, // Usage (AC Pan) 38 | 0x15, 0x81, // Logical Minimum (-127) 39 | 0x25, 0x7F, // Logical Maximum (127) 40 | 0x75, 0x08, // Report Size (8) 41 | 0x95, 0x01, // Report Count (1) 42 | 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) 43 | 44 | 0xC0, // End Collection 45 | 0xA1, 0x02, // Collection (Logical) 46 | 47 | 0x06, 0xC1, 0xFF, // Usage Page (Vendor Defined 0xFFC1) 48 | 0x15, 0x00, // Logical Minimum (0) 49 | 0x26, 0xFF, 0x00, // Logical Maximum (255) 50 | 0x75, 0x08, // Report Size (8) 51 | 0x09, 0xF0, // Usage (0xF0) 52 | 0x95, 0x02, // Report Count (2) 53 | 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 54 | 55 | 0xC0, // End Collection 56 | 0xC0, // End Collection 57 | 0xC0, // End Collection 58 | 59 | // 98 bytes 60 | -------------------------------------------------------------------------------- /debug/devices/steelseries_rival600_descriptor_raw.txt: -------------------------------------------------------------------------------- 1 | 005:004:002:DESCRIPTOR 1617489710.244663 2 | 05 01 09 06 A1 01 85 01 05 07 19 E0 29 E7 15 00 3 | 25 01 75 01 95 08 81 02 75 08 95 01 81 01 05 07 4 | 19 00 2A FF 00 15 00 26 FF 00 75 08 95 06 81 00 5 | C0 05 0C 09 01 A1 01 85 02 05 0C 19 00 2A FF 0F 6 | 15 00 26 FF 0F 75 10 95 02 81 00 C0 7 | 8 | 005:004:001:DESCRIPTOR 1617489710.245521 9 | 05 01 09 02 A1 01 09 01 A1 00 A1 02 05 09 19 01 10 | 29 08 15 00 25 01 95 08 75 01 81 02 05 01 09 30 11 | 09 31 16 01 80 26 FF 7F 75 10 95 02 81 06 09 38 12 | 15 81 25 7F 75 08 95 01 81 06 C0 A1 02 05 0C 0A 13 | 38 02 15 81 25 7F 75 08 95 01 81 06 C0 A1 02 06 14 | C1 FF 15 00 26 FF 00 75 08 09 F0 95 02 81 02 C0 15 | C0 C0 16 | 17 | 005:004:000:DESCRIPTOR 1617489710.246510 18 | 06 C0 FF 09 01 A1 01 06 C1 FF 15 00 26 FF 00 75 19 | 08 09 F0 95 20 81 02 09 F1 95 20 91 02 09 F2 96 20 | 42 02 B1 02 C0 21 | -------------------------------------------------------------------------------- /debug/devices/swiftpoint_tracer_descriptor.txt: -------------------------------------------------------------------------------- 1 | 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 2 | 0x09, 0x02, // Usage (Mouse) 3 | 0xA1, 0x01, // Collection (Application) 4 | 0x85, 0x01, // Report ID (1) 5 | 0x09, 0x01, // Usage (Pointer) 6 | 0xA1, 0x00, // Collection (Physical) 7 | 8 | 0x95, 0x10, // Report Count (16) 9 | 0x75, 0x01, // Report Size (1) 10 | 0x05, 0x09, // Usage Page (Button) 11 | 0x19, 0x01, // Usage Minimum (0x01) 12 | 0x29, 0x10, // Usage Maximum (0x10) 13 | 0x15, 0x00, // Logical Minimum (0) 14 | 0x25, 0x01, // Logical Maximum (1) 15 | 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 16 | 17 | 0x75, 0x10, // Report Size (16) 18 | 0x95, 0x02, // Report Count (2) 19 | 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 20 | 0x09, 0x30, // Usage (X) 21 | 0x09, 0x31, // Usage (Y) 22 | 0x16, 0x01, 0x80, // Logical Minimum (-32767) 23 | 0x26, 0xFF, 0x7F, // Logical Maximum (32767) 24 | 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) 25 | 26 | 0xA1, 0x02, // Collection (Logical) 27 | 0x85, 0x02, // Report ID (2) 28 | 0x09, 0x48, // Usage (0x48) 29 | 0x95, 0x01, // Report Count (1) 30 | 0x75, 0x02, // Report Size (2) 31 | 0x15, 0x00, // Logical Minimum (0) 32 | 0x25, 0x01, // Logical Maximum (1) 33 | 0x35, 0x01, // Physical Minimum (1) 34 | 0x45, 0x78, // Physical Maximum (120) 35 | 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 36 | 37 | 0x85, 0x01, // Report ID (1) 38 | 0x75, 0x10, // Report Size (16) 39 | 0x95, 0x01, // Report Count (1) 40 | 0x09, 0x38, // Usage (Wheel) 41 | 0x35, 0x00, // Physical Minimum (0) 42 | 0x45, 0x00, // Physical Maximum (0) 43 | 0x16, 0x01, 0x80, // Logical Minimum (-32767) 44 | 0x26, 0xFF, 0x7F, // Logical Maximum (32767) 45 | 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) 46 | 0xC0, // End Collection 47 | 48 | 0xA1, 0x02, // Collection (Logical) 49 | 0x85, 0x02, // Report ID (2) 50 | 0x09, 0x48, // Usage (0x48) 51 | 0x95, 0x01, // Report Count (1) 52 | 0x75, 0x02, // Report Size (2) 53 | 0x15, 0x00, // Logical Minimum (0) 54 | 0x25, 0x01, // Logical Maximum (1) 55 | 0x35, 0x01, // Physical Minimum (1) 56 | 0x45, 0x78, // Physical Maximum (120) 57 | 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 58 | 59 | 0x35, 0x00, // Physical Minimum (0) 60 | 0x45, 0x00, // Physical Maximum (0) 61 | 0x75, 0x04, // Report Size (4) 62 | 0xB1, 0x03, // Feature (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 63 | 64 | 0x85, 0x01, // Report ID (1) 65 | 0x75, 0x10, // Report Size (16) 66 | 0x95, 0x01, // Report Count (1) 67 | 0x05, 0x0C, // Usage Page (Consumer) 68 | 0x0A, 0x38, 0x02, // Usage (AC Pan) 69 | 0x16, 0x01, 0x80, // Logical Minimum (-32767) 70 | 0x26, 0xFF, 0x7F, // Logical Maximum (32767) 71 | 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) 72 | 0xC0, // End Collection 73 | 0xC0, // End Collection 74 | 0xC0, // End Collection 75 | 76 | // 137 bytes 77 | -------------------------------------------------------------------------------- /debug/devices/swiftpoint_tracer_descriptor_raw.txt: -------------------------------------------------------------------------------- 1 | 001:016:003:DESCRIPTOR 1617602116.719865 2 | 06 00 FF 09 01 A1 01 15 00 26 FF 00 85 01 75 08 3 | 95 15 09 01 B1 02 85 0A 75 08 95 3F 09 01 81 02 4 | 85 0B 75 08 95 3F 09 01 81 02 85 14 75 08 95 0F 5 | 09 01 81 02 85 17 75 08 95 24 09 01 81 02 85 28 6 | 75 08 95 3F 09 01 81 02 85 29 95 3F 09 01 91 02 7 | 85 2A 95 3F 09 01 B1 02 C0 8 | 9 | 001:016:002:DESCRIPTOR 1617602116.720347 10 | 05 01 09 04 A1 01 75 0C 95 05 15 00 26 FF 0F 35 11 | 00 46 FF 0F 09 30 09 31 09 32 09 35 09 36 81 02 12 | 75 04 95 01 25 07 46 3B 01 65 14 09 39 81 42 95 13 | 20 75 01 15 00 25 01 05 09 19 01 29 20 81 02 C0 14 | 15 | 001:016:001:DESCRIPTOR 1617602116.720780 16 | 05 01 09 06 A1 01 85 01 75 01 95 08 05 07 19 E0 17 | 29 E7 15 00 25 01 81 02 81 03 95 06 75 08 15 00 18 | 26 FF 00 05 07 19 00 2A FF 00 81 00 C0 05 0C 09 19 | 01 A1 01 85 02 75 10 95 02 15 01 26 9C 02 19 01 20 | 2A 9C 02 81 00 C0 21 | 22 | 001:016:000:DESCRIPTOR 1617602116.721236 23 | 05 01 09 02 A1 01 85 01 09 01 A1 00 95 10 75 01 24 | 05 09 19 01 29 10 15 00 25 01 81 02 75 10 95 02 25 | 05 01 09 30 09 31 16 01 80 26 FF 7F 81 06 A1 02 26 | 85 02 09 48 95 01 75 02 15 00 25 01 35 01 45 78 27 | B1 02 85 01 75 10 95 01 09 38 35 00 45 00 16 01 28 | 80 26 FF 7F 81 06 C0 A1 02 85 02 09 48 95 01 75 29 | 02 15 00 25 01 35 01 45 78 B1 02 35 00 45 00 75 30 | 04 B1 03 85 01 75 10 95 01 05 0C 0A 38 02 16 01 31 | 80 26 FF 7F 81 06 C0 C0 C0 32 | -------------------------------------------------------------------------------- /debug/devices/trust_gxt_101_gav.txt: -------------------------------------------------------------------------------- 1 | Bus 003 Device 030: ID 145f:01d9 Trust USB Optical Mouse 2 | Couldn't open device, some information will be missing 3 | Device Descriptor: 4 | bLength 18 5 | bDescriptorType 1 6 | bcdUSB 1.10 7 | bDeviceClass 0 8 | bDeviceSubClass 0 9 | bDeviceProtocol 0 10 | bMaxPacketSize0 8 11 | idVendor 0x145f Trust 12 | idProduct 0x01d9 13 | bcdDevice 1.01 14 | iManufacturer 1 USB MOUSE 15 | iProduct 2 USB Optical Mouse 16 | iSerial 0 17 | bNumConfigurations 1 18 | Configuration Descriptor: 19 | bLength 9 20 | bDescriptorType 2 21 | wTotalLength 0x0022 22 | bNumInterfaces 1 23 | bConfigurationValue 1 24 | iConfiguration 0 25 | bmAttributes 0xa0 26 | (Bus Powered) 27 | Remote Wakeup 28 | MaxPower 100mA 29 | Interface Descriptor: 30 | bLength 9 31 | bDescriptorType 4 32 | bInterfaceNumber 0 33 | bAlternateSetting 0 34 | bNumEndpoints 1 35 | bInterfaceClass 3 Human Interface Device 36 | bInterfaceSubClass 1 Boot Interface Subclass 37 | bInterfaceProtocol 2 Mouse 38 | iInterface 0 39 | HID Device Descriptor: 40 | bLength 9 41 | bDescriptorType 33 42 | bcdHID 1.10 43 | bCountryCode 0 Not supported 44 | bNumDescriptors 1 45 | bDescriptorType 34 Report 46 | wDescriptorLength 52 47 | Report Descriptors: 48 | ** UNAVAILABLE ** 49 | Endpoint Descriptor: 50 | bLength 7 51 | bDescriptorType 5 52 | bEndpointAddress 0x81 EP 1 IN 53 | bmAttributes 3 54 | Transfer Type Interrupt 55 | Synch Type None 56 | Usage Type Data 57 | wMaxPacketSize 0x0004 1x 4 bytes 58 | bInterval 10 59 | -------------------------------------------------------------------------------- /debug/devices/trust_gxt_101_gav_descriptor.txt: -------------------------------------------------------------------------------- 1 | 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 2 | 0x09, 0x02, // Usage (Mouse) 3 | 0xA1, 0x01, // Collection (Application) 4 | 0x09, 0x01, // Usage (Pointer) 5 | 0xA1, 0x00, // Collection (Physical) 6 | 7 | 0x05, 0x09, // Usage Page (Button) 8 | 0x19, 0x01, // Usage Minimum (0x01) 9 | 0x29, 0x05, // Usage Maximum (0x05) 10 | 0x15, 0x00, // Logical Minimum (0) 11 | 0x25, 0x01, // Logical Maximum (1) 12 | 0x95, 0x05, // Report Count (5) 13 | 0x75, 0x01, // Report Size (1) 14 | 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 15 | 16 | 0x95, 0x01, // Report Count (1) 17 | 0x75, 0x03, // Report Size (3) 18 | 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) 19 | 20 | 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 21 | 0x09, 0x30, // Usage (X) 22 | 0x09, 0x31, // Usage (Y) 23 | 0x09, 0x38, // Usage (Wheel) 24 | 0x15, 0x81, // Logical Minimum (-127) 25 | 0x25, 0x7F, // Logical Maximum (127) 26 | 0x75, 0x08, // Report Size (8) 27 | 0x95, 0x03, // Report Count (3) 28 | 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) 29 | 30 | 0xC0, // End Collection 31 | 0xC0, // End Collection 32 | 33 | // 52 bytes 34 | -------------------------------------------------------------------------------- /debug/devices/trust_gxt_101_gav_descriptor_raw.txt: -------------------------------------------------------------------------------- 1 | 003:032:000:DESCRIPTOR 1618180728.142853 2 | 05 01 09 02 A1 01 09 01 A1 00 05 09 19 01 29 05 3 | 15 00 25 01 95 05 75 01 81 02 95 01 75 03 81 01 4 | 05 01 09 30 09 31 09 38 15 81 25 7F 75 08 95 03 5 | 81 06 C0 C0 6 | -------------------------------------------------------------------------------- /debug/hid_parser/Readme.org: -------------------------------------------------------------------------------- 1 | * What? 2 | This is my attempt to parse a HID report descriptor for a pointer device. 3 | We are only interested in BUTTON, X, Y, and WHEEL inputs. 4 | 5 | The parser is very crude and one =should definitely not use it= and rather use the code in =hid-core= of the linux kernel source. 6 | However, right now, my understanding of =hid-core= is very limited, so I rather use this quick-and-dirty hack right now. 7 | Feel free to contribute and fix this driver, so it uses the =hid= backend. 8 | 9 | Right now, this parser has been successfully tested against several different mice with very different report descriptor responses. 10 | 11 | In order to test this script, add a custom HEX report descriptor to the to =hid_parser.h= like 12 | #+begin_src cpp 13 | #define STEELSERIES_RIVAL_600 \ 14 | 0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01, 0xA1, 0x00, 0xA1, 0x02, 0x05, 0x09, 0x19, 0x01, \ 15 | 0x29, 0x08, 0x15, 0x00, 0x25, 0x01, 0x95, 0x08, 0x75, 0x01, 0x81, 0x02, 0x05, 0x01, 0x09, 0x30, \ 16 | 0x09, 0x31, 0x16, 0x01, 0x80, 0x26, 0xFF, 0x7F, 0x75, 0x10, 0x95, 0x02, 0x81, 0x06, 0x09, 0x38, \ 17 | 0x15, 0x81, 0x25, 0x7F, 0x75, 0x08, 0x95, 0x01, 0x81, 0x06, 0xC0, 0xA1, 0x02, 0x05, 0x0C, 0x0A, \ 18 | 0x38, 0x02, 0x15, 0x81, 0x25, 0x7F, 0x75, 0x08, 0x95, 0x01, 0x81, 0x06, 0xC0, 0xA1, 0x02, 0x06, \ 19 | 0xC1, 0xFF, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x09, 0xF0, 0x95, 0x02, 0x81, 0x02, 0xC0, \ 20 | 0xC0, 0xC0 21 | #+end_src 22 | 23 | To test the parsing of the descriptor, activate it in =hid_parser.cpp=. 24 | #+begin_src cpp 25 | unsigned char desc[] = { 26 | STEELSERIES_RIVAL_600 27 | } 28 | #+end_src 29 | 30 | Then just compile the code and compare the output (offset in bits and size in bits) with the data retrieve via =usb-hiddump= and [[https://eleccelerator.com/usbdescreqparser/][this website/parser]] 31 | #+begin_src sh 32 | g++ hid_parser.cpp && ./a.out 33 | #+end_src 34 | 35 | The output should look similar to 36 | #+begin_src cfg 37 | Is tagged with report ID: No 38 | BTN (0): Offset 0 Size 8 Signed 0 39 | X (0): Offset 8 Size 16 Signed 1 40 | Y (0): Offset 24 Size 16 Signed 1 41 | WHL (0): Offset 40 Size 8 Signed 1 42 | #+end_src 43 | 44 | The program also can parse raw USB packets. Simply add a raw USB packet to =hid_parser.cpp= like 45 | #+begin_src sh 46 | unsigned char packet[] = { 47 | //### Steelseries Rival 600 48 | 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 49 | }; 50 | #+end_src 51 | 52 | You can get the corresponding report descriptor for your mouse via =usb-hiddump.= See this [[../Readme.org][Readme]] for more instructions on how to get the report descriptor. 53 | 54 | In order to get a raw packet, you can intercept them via the leetmouse driver, by commenting out the =printk= lines in =util.c:extract_mouse_events= (needs manual compilation). The code there looks like this 55 | #+begin_src cpp 56 | int i; 57 | printk(KERN_CONT "Raw: "); 58 | for(i = 0; i 6 | #include "FixedMath/Fixed64.h" 7 | 8 | #define MAX_LUT_ARRAY_SIZE 128 9 | #define MAX_LUT_BUF_LEN 4096 10 | 11 | struct ModesConstants { 12 | bool is_init; 13 | 14 | // General 15 | FP_LONG accel_sub_1; 16 | FP_LONG exp_sub_1; 17 | 18 | // Synchronous (legacy) 19 | FP_LONG logMot; 20 | FP_LONG gammaConst; 21 | FP_LONG logSync; 22 | FP_LONG sharpness; 23 | FP_LONG sharpnessRecip; 24 | bool useClamp; 25 | FP_LONG minSens; 26 | FP_LONG maxSens; 27 | 28 | // Classic 29 | FP_LONG sign; 30 | FP_LONG gain_constant; 31 | FP_LONG cap_x; 32 | FP_LONG cap_y; 33 | 34 | // Jump 35 | FP_LONG C0; // the "integral" evaluated at 0 36 | FP_LONG r; // basically a smoothness factor 37 | 38 | // Power 39 | FP_LONG offset_x; 40 | FP_LONG power_constant; 41 | 42 | // Natural 43 | FP_LONG auxiliar_accel; 44 | FP_LONG auxiliar_constant; 45 | 46 | // Rotation 47 | FP_LONG sin_a, cos_a; 48 | 49 | // Angle Snapping 50 | FP_LONG as_sin, as_cos; 51 | FP_LONG as_half_threshold; 52 | }; 53 | 54 | extern FP_LONG g_Acceleration, g_Exponent, g_Midpoint, g_Motivity, g_RotationAngle, g_AngleSnap_Angle, g_AngleSnap_Threshold, g_LutData_x[], g_LutData_y[]; 55 | extern char g_AccelerationMode, g_UseSmoothing; 56 | extern unsigned long g_LutSize; 57 | extern struct ModesConstants modesConst; 58 | static const FP_LONG FP64_PI = C0NST_FP64_FromDouble(3.14159); 59 | static const FP_LONG FP64_PI_2 = C0NST_FP64_FromDouble(1.57079); 60 | static const FP_LONG FP64_PI_4 = C0NST_FP64_FromDouble(0.78539); 61 | static const FP_LONG FP64_0_1 = 429496736ll; 62 | static const FP_LONG FP64_0_5 = 2147483648ll; 63 | static const FP_LONG FP64_1 = 1ll << FP64_Shift; 64 | static const FP_LONG FP64_10 = 10ll << FP64_Shift; 65 | static const FP_LONG FP64_100 = 100ll << FP64_Shift; 66 | static const FP_LONG FP64_1000 = 1000ll << FP64_Shift; 67 | static const FP_LONG FP64_10000 = 10000ll << FP64_Shift; 68 | 69 | void update_constants(void); 70 | 71 | FP_LONG accel_linear(FP_LONG speed); 72 | FP_LONG accel_power(FP_LONG speed); 73 | FP_LONG accel_classic(FP_LONG speed); 74 | FP_LONG accel_motivity(FP_LONG speed); 75 | FP_LONG accel_synchronous(FP_LONG speed); 76 | FP_LONG accel_natural(FP_LONG speed); 77 | FP_LONG accel_jump(FP_LONG speed); 78 | FP_LONG accel_lut(FP_LONG speed); 79 | 80 | #endif //ACCEL_MODES_H 81 | -------------------------------------------------------------------------------- /driver/config.sample.h: -------------------------------------------------------------------------------- 1 | // Acceleration Mode 2 | #define ACCELERATION_MODE AccelMode_Linear 3 | 4 | // Global Parameters 5 | #define SENSITIVITY 1 // For compatibility this is named SENSITIVITY, but it really refers just to the X axis 6 | #define SENSITIVITY_Y 1 // Only applied when this differs from the SENSITIVITY parameter 7 | #define OUTPUT_CAP 0 8 | #define INPUT_CAP 0 9 | #define OFFSET 0 10 | #define PRESCALE 1 11 | 12 | // Angle Snapping (in radians) 13 | #define ANGLE_SNAPPING_THRESHOLD 0 // 0 deg. in rad. 14 | #define ANGLE_SNAPPING_ANGLE 0 // 1.5708 - 90 deg. in rad. 15 | 16 | // Rotation (in radians) 17 | #define ROTATION_ANGLE 0 18 | 19 | // LUT settings 20 | #define LUT_SIZE 0 21 | #define LUT_DATA 0 22 | 23 | // Mode-specific parameters 24 | #define ACCELERATION 0.15 25 | #define MIDPOINT 2 26 | #define MOTIVITY 1.5 27 | #define EXPONENT 0.2 28 | #define USE_SMOOTHING 1 // 1 - True, 0 - False -------------------------------------------------------------------------------- /driver/driver.c: -------------------------------------------------------------------------------- 1 | #include "accel.h" 2 | #include "config.h" 3 | #include "util.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define NONE_EVENT_VALUE 0 14 | 15 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 11, 0)) 16 | #define __cleanup_events 0 17 | #else 18 | #define __cleanup_events 1 19 | #endif 20 | 21 | struct mouse_state { 22 | int x; 23 | int y; 24 | int wheel; 25 | }; 26 | 27 | #if __cleanup_events 28 | static unsigned int driver_events(struct input_handle *handle, struct input_value *vals, unsigned int count) { 29 | #else 30 | static void driver_events(struct input_handle *handle, const struct input_value *vals, unsigned int count) { 31 | #endif 32 | struct mouse_state *state = handle->private; 33 | struct input_dev *dev = handle->dev; 34 | unsigned int out_count = count; 35 | struct input_value *v_syn = NULL; 36 | struct input_value *end = (struct input_value *) vals; 37 | struct input_value *v; 38 | int error; 39 | 40 | for (v = (struct input_value *) vals; v != vals + count; v++) { 41 | if (v->type == EV_REL) { 42 | /* Find input_value for EV_REL events we're interested in and store values */ 43 | switch (v->code) { 44 | case REL_X: 45 | state->x = (int) v->value; 46 | break; 47 | case REL_Y: 48 | state->y = (int) v->value; 49 | break; 50 | case REL_WHEEL: 51 | state->wheel = (int) v->value; 52 | break; 53 | } 54 | } else if ( 55 | (state->x != NONE_EVENT_VALUE || state->y != NONE_EVENT_VALUE) && 56 | v->type == EV_SYN && v->code == SYN_REPORT 57 | ) { 58 | /* If we find an EV_SYN event, and we've seen x/y values, we store the pointer and apply acceleration next */ 59 | v_syn = v; 60 | break; 61 | } 62 | } 63 | 64 | if (v_syn != NULL) { 65 | /* Retrieve to state if an EV_SYN event was found and apply acceleration */ 66 | int x = state->x; 67 | int y = state->y; 68 | int wheel = state->wheel; 69 | /* If we found no values to update, return */ 70 | if (x == NONE_EVENT_VALUE && y == NONE_EVENT_VALUE && wheel == NONE_EVENT_VALUE) 71 | goto unchanged_return; 72 | error = accelerate(&x, &y); 73 | /* Reset state */ 74 | state->x = NONE_EVENT_VALUE; 75 | state->y = NONE_EVENT_VALUE; 76 | state->wheel = NONE_EVENT_VALUE; 77 | /* Deal with left over EV_REL events we should take into account for the next run */ 78 | for (v = v_syn; v != vals + count; v++) { 79 | if (v->type == EV_REL) { 80 | /* Store values for next runthrough */ 81 | switch (v->code) { 82 | case REL_X: 83 | state->x = v->value; 84 | break; 85 | case REL_Y: 86 | state->y = v->value; 87 | break; 88 | case REL_WHEEL: 89 | state->wheel = v->value; 90 | break; 91 | } 92 | } 93 | } 94 | /* Apply updates after we've captured events for next run */ 95 | if (!error) { 96 | for (v = (struct input_value *) vals; v != vals + count; v++) { 97 | if (v->type == EV_REL) { 98 | switch (v->code) { 99 | case REL_X: 100 | if (__cleanup_events && x == NONE_EVENT_VALUE) 101 | continue; 102 | v->value = x; 103 | break; 104 | case REL_Y: 105 | if (__cleanup_events && y == NONE_EVENT_VALUE) 106 | continue; 107 | v->value = y; 108 | break; 109 | case REL_WHEEL: 110 | if (__cleanup_events && wheel == NONE_EVENT_VALUE) 111 | continue; 112 | v->value = wheel; 113 | break; 114 | } 115 | } 116 | if (end != v) 117 | *end = *v; 118 | end++; 119 | } 120 | out_count = end - vals; 121 | /* Apply new values to the queued (raw) events, same as above. 122 | * NOTE: This might (strictly speaking) not be necessary, but this way we leave 123 | * no trace of the unmodified values, in case another subsystem uses them. */ 124 | for (v = (struct input_value *) vals; v != vals + count; v++) { 125 | if (v->type == EV_REL) { 126 | switch (v->code) { 127 | case REL_X: 128 | if (x == NONE_EVENT_VALUE) 129 | continue; 130 | v->value = x; 131 | break; 132 | case REL_Y: 133 | if (y == NONE_EVENT_VALUE) 134 | continue; 135 | v->value = y; 136 | break; 137 | case REL_WHEEL: 138 | if (wheel == NONE_EVENT_VALUE) 139 | continue; 140 | v->value = wheel; 141 | break; 142 | } 143 | } 144 | if (end != v) 145 | *end = *v; 146 | end++; 147 | } 148 | dev->num_vals = end - vals; 149 | } 150 | } 151 | /* NOTE: Technically, we can also stop iterating over `vals` when we find EV_SYN, apply acceleration, 152 | * then restart in a loop until we reach the end of `vals` to handle multiple EV_SYN events per batch. 153 | * However, that's not necessary since we can assume that all events in `vals` apply to the same moment 154 | * in time. */ 155 | unchanged_return: 156 | #if __cleanup_events 157 | return out_count; 158 | #else 159 | return; 160 | #endif 161 | } 162 | 163 | static bool driver_match(struct input_handler *handler, struct input_dev *dev) { 164 | if (!dev->dev.parent) 165 | return false; 166 | struct hid_device *hdev = to_hid_device(dev->dev.parent); 167 | printk("Yeetmouse: found a possible mouse %s", hdev->name); 168 | //return hdev->type == HID_TYPE_USBMOUSE; // This only detects USB mice, not bluetooth, or other mice (like PS/2) 169 | 170 | // Discard if doesn't have left button key capabilities 171 | if (!test_bit(EV_KEY, dev->evbit) || !test_bit(BTN_LEFT, dev->keybit) || !test_bit(BTN_MOUSE, dev->keybit)) 172 | return false; 173 | 174 | // Discard if doesn't have relative movement capabilities 175 | if (!test_bit(EV_REL, dev->evbit) || !test_bit(REL_X, dev->relbit) || !test_bit(REL_Y, dev->relbit)) 176 | return false; 177 | 178 | // Discard if doesn't have scroll capabilities (should not be necessary) 179 | // if (!test_bit(REL_WHEEL, dev->relbit)) 180 | // return false; 181 | 182 | // This still might permit some tablets 183 | return true; 184 | } 185 | 186 | /* Same as Linux's input_register_handle but we always add the handle to the head of handlers */ 187 | int input_register_handle_head(struct input_handle *handle) { 188 | struct input_handler *handler = handle->handler; 189 | struct input_dev *dev = handle->dev; 190 | 191 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 11, 7)) 192 | /* In 6.11.7 an additional handler pointer was added: https://github.com/torvalds/linux/commit/071b24b54d2d05fbf39ddbb27dee08abd1d713f3 */ 193 | if (handler->events) 194 | handle->handle_events = handler->events; 195 | #endif 196 | 197 | int error = mutex_lock_interruptible(&dev->mutex); 198 | if (error) 199 | return error; 200 | list_add_rcu(&handle->d_node, &dev->h_list); 201 | mutex_unlock(&dev->mutex); 202 | list_add_tail_rcu(&handle->h_node, &handler->h_list); 203 | if (handler->start) 204 | handler->start(handle); 205 | return 0; 206 | } 207 | 208 | static int driver_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) { 209 | struct input_handle *handle; 210 | struct mouse_state *state; 211 | int error; 212 | 213 | handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); 214 | if (!handle) 215 | return -ENOMEM; 216 | 217 | state = kzalloc(sizeof(struct mouse_state), GFP_KERNEL); 218 | if (!state) { 219 | kfree(handle); 220 | return -ENOMEM; 221 | } 222 | 223 | state->x = NONE_EVENT_VALUE; 224 | state->y = NONE_EVENT_VALUE; 225 | state->wheel = NONE_EVENT_VALUE; 226 | 227 | handle->private = state; 228 | handle->dev = input_get_device(dev); 229 | handle->handler = handler; 230 | handle->name = "yeetmouse"; 231 | 232 | /* WARN: Instead of `input_register_handle` we use a customized version of it here. 233 | * This prepends the handler (like a filter) instead of appending it, making 234 | * it take precedence over any other input handler that'll be added. */ 235 | error = input_register_handle_head(handle); 236 | if (error) 237 | goto err_free_mem; 238 | 239 | error = input_open_device(handle); 240 | if (error) 241 | goto err_unregister_handle; 242 | 243 | printk(pr_fmt("Yeetmouse: connecting to device: %s (%s at %s)"), dev_name(&dev->dev), dev->name ?: "unknown", 244 | dev->phys ?: "unknown"); 245 | 246 | return 0; 247 | 248 | err_unregister_handle: 249 | input_unregister_handle(handle); 250 | 251 | err_free_mem: 252 | kfree(handle->private); 253 | kfree(handle); 254 | return error; 255 | } 256 | 257 | static void driver_disconnect(struct input_handle *handle) { 258 | input_close_device(handle); 259 | input_unregister_handle(handle); 260 | kfree(handle->private); 261 | kfree(handle); 262 | } 263 | 264 | static const struct input_device_id driver_ids[] = { 265 | { 266 | .flags = INPUT_DEVICE_ID_MATCH_EVBIT, 267 | .evbit = {BIT_MASK(EV_REL)} 268 | }, 269 | {}, 270 | }; 271 | 272 | MODULE_DEVICE_TABLE(input, driver_ids); 273 | 274 | struct input_handler driver_handler = { 275 | .name = "yeetmouse", 276 | .id_table = driver_ids, 277 | .events = driver_events, 278 | .connect = driver_connect, 279 | .disconnect = driver_disconnect, 280 | .match = driver_match 281 | }; 282 | 283 | static int __init yeetmouse_init(void) { 284 | return input_register_handler(&driver_handler); 285 | } 286 | 287 | static void __exit yeetmouse_exit(void) { 288 | input_unregister_handler(&driver_handler); 289 | } 290 | 291 | MODULE_DESCRIPTION("USB HID input handler applying mouse acceleration (Yeetmouse)"); 292 | MODULE_LICENSE("GPL"); 293 | 294 | module_init(yeetmouse_init); 295 | module_exit(yeetmouse_exit); 296 | -------------------------------------------------------------------------------- /driver/util.h: -------------------------------------------------------------------------------- 1 | #ifndef _UTIL_H 2 | #define _UTIL_H 3 | 4 | #define INLINE __attribute__((always_inline)) inline 5 | 6 | #endif //_UTIL_H 7 | -------------------------------------------------------------------------------- /gui/ConfigHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "ConfigHelper.h" 2 | #include 3 | #include 4 | #include 5 | 6 | char *OpenFile() { 7 | char *filename = new char[512]; 8 | char cwd[1024]; 9 | char command[2048] = R"(zenity --file-selection --title="Select a config file" 2> /dev/null)"; 10 | FILE *f = nullptr; 11 | if (getcwd(cwd, sizeof(cwd)) != nullptr) 12 | sprintf(command, R"(zenity --file-selection --title="Select a config file" --filename="%s/" 2> /dev/null)", 13 | cwd); 14 | 15 | f = popen(command, "r"); 16 | auto res = fgets(filename, 512, f); 17 | if (!res) { 18 | delete[] filename; 19 | return nullptr; 20 | } 21 | res[strlen(res) - 1] = 0; 22 | 23 | pclose(f); 24 | 25 | return res; 26 | } 27 | 28 | char *SaveFile() { 29 | char *filename = new char[512]; 30 | char cwd[1024]; 31 | char command[2048] = R"(zenity --save --file-selection --title="Save Config" 2> /dev/null)"; 32 | FILE *f = nullptr; 33 | if (getcwd(cwd, sizeof(cwd)) != nullptr) 34 | sprintf(command, R"(zenity --save --file-selection --title="Save Config" --filename="%s/" 2> /dev/null)", cwd); 35 | 36 | f = popen(command, "r"); 37 | auto res = fgets(filename, 512, f); 38 | if (!res) { 39 | delete[] filename; 40 | return nullptr; 41 | } 42 | res[strlen(res) - 1] = 0; 43 | 44 | pclose(f); 45 | 46 | return res; 47 | } 48 | 49 | namespace ConfigHelper { 50 | std::string ExportPlainText(Parameters params, bool save_to_file) { 51 | std::stringstream res_ss; 52 | 53 | try { 54 | res_ss << "sens=" << params.sens << std::endl; 55 | res_ss << "sens_Y=" << (params.use_anisotropy ? params.sensY : params.sens) << std::endl; 56 | res_ss << "outCap=" << params.outCap << std::endl; 57 | res_ss << "inCap=" << params.inCap << std::endl; 58 | res_ss << "offset=" << params.offset << std::endl; 59 | res_ss << "accel=" << params.accel << std::endl; 60 | res_ss << "exponent=" << params.exponent << std::endl; 61 | res_ss << "midpoint=" << params.midpoint << std::endl; 62 | res_ss << "motivity=" << params.motivity << std::endl; 63 | res_ss << "preScale=" << params.preScale << std::endl; 64 | res_ss << "accelMode=" << AccelMode2EnumString(params.accelMode) << std::endl; 65 | res_ss << "useSmoothing=" << params.useSmoothing << std::endl; 66 | res_ss << "rotation=" << params.rotation << std::endl; 67 | res_ss << "as_threshold=" << params.as_threshold << std::endl; 68 | res_ss << "as_angle=" << params.as_angle << std::endl; 69 | res_ss << "LUT_size=" << params.LUT_size << std::endl; 70 | res_ss << "LUT_data=" << DriverHelper::EncodeLutData(params.LUT_data_x, params.LUT_data_y, params.LUT_size); 71 | 72 | if (save_to_file) { 73 | auto out_path = SaveFile(); 74 | if (!out_path) 75 | return ""; 76 | std::ofstream out_file(out_path); 77 | 78 | delete[] out_path; 79 | 80 | if (!out_file.good()) 81 | return ""; 82 | 83 | out_file << res_ss.str(); 84 | 85 | out_file.close(); 86 | } 87 | return res_ss.str(); 88 | } catch (std::exception &ex) { 89 | printf("Failed Export: %s\n", ex.what()); 90 | } 91 | 92 | return ""; 93 | } 94 | 95 | std::string ExportConfig(Parameters params, bool save_to_file) { 96 | try { 97 | std::stringstream res_ss; 98 | 99 | res_ss << "#define SENSITIVITY " << params.sens << std::endl; 100 | res_ss << "#define SENSITIVITY_Y " << (params.use_anisotropy ? params.sensY : params.sens) << std::endl; 101 | res_ss << "#define OUTPUT_CAP " << params.outCap << std::endl; 102 | res_ss << "#define INPUT_CAP " << params.inCap << std::endl; 103 | res_ss << "#define OFFSET " << params.offset << std::endl; 104 | res_ss << "#define ACCELERATION " << params.accel << std::endl; 105 | res_ss << "#define EXPONENT " << params.exponent << std::endl; 106 | res_ss << "#define MIDPOINT " << params.midpoint << std::endl; 107 | res_ss << "#define MOTIVITY " << params.motivity << std::endl; 108 | res_ss << "#define PRESCALE " << params.preScale << std::endl; 109 | res_ss << "#define ACCELERATION_MODE " << AccelMode2EnumString(params.accelMode) << std::endl; 110 | res_ss << "#define USE_SMOOTHING " << params.useSmoothing << std::endl; 111 | res_ss << "#define ROTATION_ANGLE " << (params.rotation * DEG2RAD) << std::endl; 112 | res_ss << "#define ANGLE_SNAPPING_THRESHOLD " << (params.as_threshold * DEG2RAD) << std::endl; 113 | res_ss << "#define ANGLE_SNAPPING_ANGLE " << (params.as_angle * DEG2RAD) << std::endl; 114 | res_ss << "#define LUT_SIZE " << params.LUT_size << std::endl; 115 | res_ss << "#define LUT_DATA " << DriverHelper::EncodeLutData( 116 | params.LUT_data_x, params.LUT_data_y, params.LUT_size); 117 | 118 | if (save_to_file) { 119 | auto out_path = SaveFile(); 120 | if (!out_path) 121 | return ""; 122 | std::ofstream out_file(out_path); 123 | 124 | delete[] out_path; 125 | 126 | if (!out_file.good()) 127 | return ""; 128 | 129 | out_file << res_ss.str(); 130 | 131 | out_file.close(); 132 | } 133 | return res_ss.str(); 134 | } catch (std::exception &ex) { 135 | printf("Failed Export: %s\n", ex.what()); 136 | } 137 | 138 | return ""; 139 | } 140 | 141 | #define STRING_2_LOWERCASE(s) std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c){ return std::tolower(c); }); 142 | 143 | template 144 | std::optional ImportAny(StreamType &stream, char *lut_data, bool &is_config_h, 145 | bool *is_old_config = nullptr) { 146 | static_assert(std::is_base_of::value, "StreamType must be derived from std::istream"); 147 | 148 | Parameters params; 149 | 150 | int unknown_params = 0; 151 | std::string line; 152 | int idx = 0; 153 | while (getline(stream, line)) { 154 | if (idx == 0 && (line.find("#define") != std::string::npos || line.find("//") != std::string::npos)) 155 | is_config_h = true; 156 | 157 | if (is_config_h && line[0] == '/' && line[1] == '/') 158 | continue; 159 | 160 | std::string name; 161 | std::string val_str; 162 | double val = 0; 163 | 164 | std::string part; 165 | std::stringstream ss(line); 166 | for (int part_idx = 0; ss >> part; part_idx++) { 167 | if (is_config_h) { 168 | if (part_idx == 0) 169 | continue; 170 | else if (part_idx == 1) { 171 | name = part; 172 | STRING_2_LOWERCASE(name); 173 | } else if (part_idx == 2) { 174 | val_str = part; 175 | try { 176 | val = std::stod(val_str); 177 | } catch (std::invalid_argument &_) { 178 | val = NAN; 179 | } 180 | } else 181 | continue; 182 | } else { 183 | name = part.substr(0, part.find('=')); 184 | STRING_2_LOWERCASE(name); 185 | val_str = part.substr(part.find('=') + 1); 186 | //printf("val str = %s\n", val_str.c_str()); 187 | if (!val_str.empty()) { 188 | try { 189 | val = std::stod(val_str); 190 | } catch (std::invalid_argument &_) { 191 | val = NAN; 192 | } 193 | } 194 | } 195 | } 196 | 197 | if (name == "sens" || name == "sensitivity") 198 | params.sens = val; 199 | else if (name == "sens_y" || name == "sensitivity_y") { 200 | params.sensY = val; 201 | } else if (name == "outcap" || name == "output_cap") 202 | params.outCap = val; 203 | else if (name == "incap" || name == "input_cap") 204 | params.inCap = val; 205 | else if (name == "offset" || name == "output_cap") 206 | params.offset = val; 207 | else if (name == "acceleration" || name == "accel") 208 | params.accel = val; 209 | else if (name == "exponent") 210 | params.exponent = val; 211 | else if (name == "midpoint") 212 | params.midpoint = val; 213 | else if (name == "motivity") 214 | params.motivity = val; 215 | else if (name == "prescale") 216 | params.preScale = val; 217 | else if (name == "accelmode" || name == "acceleration_mode") { 218 | if (!std::isnan(val)) { 219 | // val +2 below for backward compatibility 220 | if (is_old_config) 221 | *is_old_config = true; 222 | params.accelMode = static_cast(std::clamp( 223 | (int) val + (val > 4 ? 2 : 0), 0, (int) AccelMode_Count - 1)); 224 | } else { 225 | if (is_old_config) 226 | *is_old_config = false; 227 | params.accelMode = AccelMode_From_EnumString(val_str); 228 | } 229 | } else if (name == "usesmoothing" || name == "use_smoothing") 230 | params.useSmoothing = val; 231 | else if (name == "rotation" || name == "rotation_angle") 232 | params.rotation = val / (is_config_h ? DEG2RAD : 1); 233 | else if (name == "as_threshold" || name == "angle_snapping_threshold") 234 | params.as_threshold = val / (is_config_h ? DEG2RAD : 1); 235 | else if (name == "as_angle" || name == "angle_snapping_angle") 236 | params.as_angle = val / (is_config_h ? DEG2RAD : 1); 237 | else if (name == "lut_size") 238 | params.LUT_size = val; 239 | else if (name == "lut_data") { 240 | strcpy(lut_data, val_str.c_str()); 241 | params.LUT_size = DriverHelper::ParseUserLutData(lut_data, params.LUT_data_x, params.LUT_data_y, 242 | params.LUT_size); 243 | //DriverHelper::ParseDriverLutData(lut_data, params.LUT_data_x, params.LUT_data_y); 244 | } else 245 | unknown_params++; 246 | 247 | idx++; 248 | } 249 | 250 | params.use_anisotropy = params.sensY != params.sens; 251 | 252 | if (idx < 14 && unknown_params > 3 || unknown_params == idx) { 253 | printf("Bad config format, missing parameters\n"); 254 | return {}; 255 | } 256 | 257 | return params; 258 | } 259 | 260 | bool ImportFile(char *lut_data, Parameters ¶ms) { 261 | const char *filepath = OpenFile(); 262 | 263 | if (filepath == nullptr) 264 | return false; 265 | 266 | bool is_config_h = false; 267 | auto file_name_len = strlen(filepath); 268 | try { 269 | is_config_h = filepath[file_name_len - 1] == 'h' && filepath[file_name_len - 2] == '.'; 270 | 271 | std::fstream file(filepath); 272 | 273 | if (!file.good()) 274 | return {}; 275 | 276 | bool is_old_config = false; 277 | if (auto res = ImportAny(file, lut_data, is_config_h, &is_old_config)) 278 | params = res.value(); 279 | else { 280 | file.close(); 281 | return false; 282 | } 283 | 284 | file.close(); 285 | 286 | // Automatically re-export in the correct format 287 | if (is_old_config) { 288 | std::ofstream out_file(filepath); 289 | 290 | if (out_file.is_open()) { 291 | if (is_config_h) 292 | out_file << ExportConfig(params, false); 293 | else 294 | out_file << ExportPlainText(params, false); 295 | 296 | out_file.close(); 297 | } 298 | } 299 | 300 | delete[] filepath; 301 | } catch (std::exception &ex) { 302 | delete[] filepath; 303 | printf("Import error: %s\n", ex.what()); 304 | return false; 305 | } 306 | 307 | return true; 308 | } 309 | 310 | bool ImportClipboard(char *lut_data, const char *clipboard, Parameters ¶ms) { 311 | if (clipboard == nullptr) 312 | return false; 313 | 314 | bool is_config_h = false; 315 | try { 316 | std::stringstream sstream(clipboard); 317 | 318 | bool is_old_config = false; 319 | if (auto res = ImportAny(sstream, lut_data, is_config_h, &is_old_config)) 320 | params = res.value(); 321 | else 322 | return false; 323 | 324 | // Automatically re-export in the correct format 325 | if (is_old_config) { 326 | if (is_config_h) 327 | ImGui::SetClipboardText(ExportConfig(params, false).c_str()); 328 | else 329 | ImGui::SetClipboardText(ExportPlainText(params, false).c_str()); 330 | } 331 | } catch (std::exception &ex) { 332 | printf("Import error: %s\n", ex.what()); 333 | return false; 334 | } 335 | 336 | return true; 337 | } 338 | } // ConfigHelper 339 | -------------------------------------------------------------------------------- /gui/ConfigHelper.h: -------------------------------------------------------------------------------- 1 | #ifndef GUI_CONFIGHELPER_H 2 | #define GUI_CONFIGHELPER_H 3 | 4 | #include "DriverHelper.h" 5 | 6 | namespace ConfigHelper { 7 | std::string ExportPlainText(Parameters params, bool save_to_file); 8 | 9 | std::string ExportConfig(Parameters params, bool save_to_file); 10 | 11 | bool ImportFile(char *lut_data, Parameters ¶ms); 12 | 13 | bool ImportClipboard(char *lut_data, const char *clipboard, Parameters ¶ms); 14 | } // ConfigHelper 15 | 16 | #endif //GUI_CONFIGHELPER_H 17 | -------------------------------------------------------------------------------- /gui/CustomCurve.cpp: -------------------------------------------------------------------------------- 1 | #include "CustomCurve.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "DriverHelper.h" 7 | #include "External/ImGui/imgui_internal.h" 8 | 9 | void CustomCurve::ApplyCurveConstraints() { 10 | for (int i = 0; i < points.size(); i++) { 11 | float p_min = i > 0 ? points[i - 1].x + 0.5f : 0; 12 | float p_max = i < points.size() - 1 ? points[i + 1].x - 0.5f : 1000; 13 | 14 | points[i].x = std::clamp(points[i].x, p_min, p_max); 15 | 16 | if (i < points.size() - 1) 17 | control_points[i][0].x = std::clamp(control_points[i][0].x, 18 | points[i].x + CURVE_POINTS_MARGIN, 19 | points[i + 1].x - CURVE_POINTS_MARGIN); 20 | if (i > 0) 21 | control_points[i - 1][1].x = std::clamp(control_points[i - 1][1].x, 22 | points[i - 1].x + CURVE_POINTS_MARGIN, 23 | points[i].x - CURVE_POINTS_MARGIN); 24 | } 25 | } 26 | 27 | // Cubic Bezier derivatives 28 | ImVec2 BezierFirstOrderDerivative(ImVec2 p0, ImVec2 p1, ImVec2 p2, ImVec2 p3, float t) { 29 | float u = (1 - t); 30 | float w1 = 3 * (u * u); 31 | float w2 = 6 * (u * t); 32 | float w3 = 3 * (t * t); 33 | return ((p1 - p0) * w1) + ((p2 - p1) * w2) + ((p3 - p2) * w3); 34 | } 35 | 36 | ImVec2 BezierSecondOrderDerivative(ImVec2 p0, ImVec2 p1, ImVec2 p2, ImVec2 p3, float t) { 37 | float u = (1 - t); 38 | float w1 = 6 * u; 39 | float w2 = 6 * t; 40 | return (p2 - p1 * 2 + p0) * w1 + (p3 - p2 * 2 + p1) * w2; 41 | } 42 | 43 | // Spreads points based on the rate of change and other things 44 | // Sorry for the shear number of magic numbers in this function, there is just a lot to configure 45 | int CustomCurve::ExportCurveToLUT(double *LUT_data_x, double *LUT_data_y) const { 46 | const float TIME_ADAPTIVE_FACTOR = 0.5; 47 | const int PRE_LUT_ARRAY_SIZE = 100; 48 | const float LENGTH_WEIGHT = 0.95; 49 | int LUT_size = 0; 50 | 51 | if (points.size() <= 1) 52 | return LUT_size; 53 | 54 | LUT_size = 0; 55 | 56 | std::vector curve_arc_len((points.size() - 1) * PRE_LUT_ARRAY_SIZE); 57 | size_t idx = 0; 58 | std::vector seg_len(points.size() - 1); // normalized 59 | double len_sum = 0; 60 | // Pre calculate each segments' length 61 | auto last_p = points[0]; 62 | for (int i = 0; i < points.size() - 1; i++) { 63 | seg_len[i] = 0; 64 | curve_arc_len[idx++] = 0; 65 | for (int j = 1; j < PRE_LUT_ARRAY_SIZE; j++) { 66 | const double t = j / (PRE_LUT_ARRAY_SIZE - 1.0); 67 | ImVec2 p = ImBezierCubicCalc(points[i], control_points[i][0], control_points[i][1], points[i + 1], t); 68 | ImVec2 dist = p - last_p; 69 | dist.y *= 30; // The graph is stretched horizontally, this accounts for that 70 | curve_arc_len[idx] = curve_arc_len[idx - 1] + std::sqrt(ImLengthSqr(dist)); 71 | seg_len[i] += std::sqrt(ImLengthSqr(dist)); 72 | last_p = p; 73 | idx++; 74 | } 75 | len_sum += seg_len[i]; 76 | } 77 | 78 | // Normalize lengths [0,1] 79 | for (auto &seg: seg_len) 80 | seg = seg / len_sum * (points.size() - 1); 81 | 82 | // Linearize Bezier's domain (http://www.planetclegg.com/projects/WarpingTextToSplines.html) 83 | // Make a LUT based on arc length of the Bezier curve for later lookup 84 | auto linear_map_t = [&curve_arc_len](float tg_arc_len, size_t start) { 85 | int l = 0, r = PRE_LUT_ARRAY_SIZE - 1; 86 | int index = 0; 87 | while (l < r) { 88 | index = (l + r) / 2; 89 | if (curve_arc_len[start + index] < tg_arc_len) { 90 | l = index + 1; 91 | } else { 92 | r = index; 93 | } 94 | } 95 | if (curve_arc_len[start + index] > tg_arc_len) { 96 | index--; 97 | } 98 | 99 | float lengthBefore = curve_arc_len[start + index]; 100 | if (lengthBefore == tg_arc_len) { 101 | return (float) index / (PRE_LUT_ARRAY_SIZE - 1); 102 | } 103 | 104 | // interpolate 105 | return (index + (tg_arc_len - lengthBefore) / (curve_arc_len[start + index + 1] - lengthBefore)) / ( 106 | PRE_LUT_ARRAY_SIZE - 1); 107 | }; 108 | 109 | // Using premade LUT 110 | double u = 0; // [0,1] 111 | constexpr double U_STEP_FACTOR = 0.1; 112 | constexpr double u_step = U_STEP_FACTOR / (MAX_LUT_ARRAY_SIZE - 1); 113 | float last_added_u = 0; 114 | double target_u = 0; // Point at which to actually add a point (dynamically changes) 115 | for (int i = 0; LUT_size < (MAX_LUT_ARRAY_SIZE - 1) && u < 1.f; i++) { 116 | float t = 0; 117 | //u = ((float)i / (MAX_LUT_ARRAY_SIZE - 1)); 118 | 119 | int edge_idx = (int) (u * (points.size() - 1)); 120 | 121 | float local_u = u * (points.size() - 1) - edge_idx; 122 | //printf("u = %f, edge_idx = %d, local_u = %f\n", u, edge_idx, local_u); 123 | float tg_arc_len = local_u * curve_arc_len[(edge_idx + 1) * PRE_LUT_ARRAY_SIZE - 1]; 124 | 125 | int start = edge_idx * PRE_LUT_ARRAY_SIZE; 126 | t = linear_map_t(tg_arc_len, start); 127 | 128 | auto p = ImBezierCubicCalc(points[edge_idx], control_points[edge_idx][0], control_points[edge_idx][1], 129 | points[edge_idx + 1], t); 130 | auto dp = BezierFirstOrderDerivative(points[edge_idx], control_points[edge_idx][0], control_points[edge_idx][1], 131 | points[edge_idx + 1], t); 132 | auto d2p = BezierSecondOrderDerivative(points[edge_idx], control_points[edge_idx][0], 133 | control_points[edge_idx][1], points[edge_idx + 1], t); 134 | 135 | // rate of change = curvature of the curve at time t 136 | float rate_of_change = 1; 137 | if (i > 0) { 138 | rate_of_change = std::sqrt( 139 | std::fabs(dp.x * d2p.y - dp.y * d2p.x) / std::pow(dp.y * dp.y * 200 + dp.x * dp.x, 3.0 / 2.0) * 250); 140 | // Curvature with respect to t, + a sqrt to remap change the characteristics 141 | } 142 | float rate_of_change_clamped = ImClamp(rate_of_change, 0.f, 1.0f); 143 | 144 | if (u + 0.000002 >= target_u) { 145 | LUT_data_x[LUT_size] = p.x; 146 | LUT_data_y[LUT_size] = p.y; 147 | 148 | LUT_size++; 149 | 150 | last_added_u = u; 151 | } 152 | 153 | if (t <= 0.0001) 154 | u = (double) i / (MAX_LUT_ARRAY_SIZE - 1) * U_STEP_FACTOR + u_step; 155 | // recalibrate position, it's like with IMUs and GPS, this is the GPS 156 | else 157 | u += u_step; 158 | 159 | // Adaptive points distribution 160 | // Function f that determines how much points to put on a given edge depending on how close (slow) it is. 161 | // This function (f) has to satisfy the following statement Integral{0->1}f(x)dx = 1 162 | // For example f(x) = ax + (1-0.5a) 163 | double adaptive_time_step = (TIME_ADAPTIVE_FACTOR * p.x / points.back().x + (1.f - TIME_ADAPTIVE_FACTOR * 0.5f)) 164 | * u_step; 165 | double next_u_step = adaptive_time_step / ImLerp(1., seg_len[edge_idx], LENGTH_WEIGHT); 166 | target_u = last_added_u + ImLerp(next_u_step * 4, next_u_step * 0.9, rate_of_change_clamped) / U_STEP_FACTOR; 167 | // Clamp u to the start of the next point [u = edge / (size-1)] 168 | if (u + 0.000002 > (edge_idx + 1.0f) / (points.size() - 1)) { 169 | target_u = (edge_idx + 1.0f) / (points.size() - 1); 170 | u = target_u + 0.00002; 171 | } 172 | } 173 | 174 | LUT_data_x[LUT_size] = points.back().x; 175 | LUT_data_y[LUT_size++] = points.back().y; 176 | 177 | return LUT_size; 178 | } 179 | 180 | // https://www.codeproject.com/Articles/31859/Draw-a-Smooth-Curve-through-a-Set-of-2D-Points-wit 181 | void CustomCurve::SmoothBezier() { 182 | //bezier_control_points.clear(); 183 | auto ¢er_points = points; 184 | auto &bezier_control_points = control_points; 185 | 186 | int n = center_points.size() - 1; 187 | if (n < 1) { 188 | bezier_control_points.clear(); 189 | return; 190 | } 191 | 192 | if (n == 1) { 193 | bezier_control_points.resize(2); 194 | // Special case: Bezier curve should be a straight line. 195 | 196 | // 3P1 = 2P0 + P3 197 | bezier_control_points[0][0] = (center_points[0] * 2 + center_points[1]) / 3; 198 | 199 | // P2 = 2P1 – P0 200 | bezier_control_points[0][1] = bezier_control_points[0][0] * 2 - center_points[0]; 201 | 202 | return; 203 | } 204 | 205 | center_points.emplace_back(center_points[n] * 2 - center_points[n - 1]); // Push dummy point at the end 206 | 207 | n += 1; 208 | bezier_control_points.resize(n); 209 | 210 | // Calculate first Bezier control points 211 | // Right hand side vector 212 | ImVec2 *rhs = new ImVec2[n]; 213 | 214 | // Set right hand side X values 215 | for (int i = 1; i < n - 1; ++i) 216 | rhs[i] = center_points[i] * 4 + center_points[i + 1] * 2; 217 | rhs[0] = center_points[0] + center_points[1] * 2; 218 | rhs[n - 1] = (center_points[n - 1] * 8 + center_points[n]) / 2.0; 219 | // Get first control points 220 | 221 | ImVec2 *x = new ImVec2[n]; // Solution vector. 222 | ImVec2 *tmp = new ImVec2[n]; // Temp workspace. 223 | 224 | ImVec2 b = {2.0, 2.0}; 225 | x[0] = rhs[0] / b; 226 | for (int i = 1; i < n; i++) // Decomposition and forward substitution. 227 | { 228 | tmp[i] = ImVec2(1, 1) / b; 229 | b = (i < n - 1 ? ImVec2(4, 4) : ImVec2(3.5, 3.5)) - tmp[i]; 230 | x[i] = (rhs[i] - x[i - 1]) / b; 231 | } 232 | for (int i = 1; i < n; i++) 233 | x[n - i - 1] -= tmp[n - i] * x[n - i]; // Backsubstitution. 234 | 235 | 236 | for (int i = 0; i < n; ++i) { 237 | // First control point 238 | if (!center_points[i].is_locked) 239 | bezier_control_points[i][0] = x[i]; 240 | // Second control point 241 | if (i == n - 1 || !center_points[i + 1].is_locked) { 242 | if (i < n - 1) { 243 | bezier_control_points[i][1] = center_points[i + 1] * 2 - x[i + 1]; 244 | } else 245 | bezier_control_points[i][1] = center_points[n] + x[n - 1] / 2; 246 | } 247 | } 248 | 249 | center_points.pop_back(); // Pop the back dummy 250 | bezier_control_points.pop_back(); 251 | 252 | ApplyCurveConstraints(); 253 | 254 | delete[] rhs; 255 | delete[] x; 256 | delete[] tmp; 257 | } 258 | 259 | void CustomCurve::UpdateLUT() { 260 | if (points.size() < 2) 261 | return; 262 | LUT_points.resize((points.size() - 1) * BEZIER_FRAG_SEGMENTS); 263 | ImVec2 last_point = points[0]; 264 | for (int i = 0; i < points.size() - 1; i++) { 265 | for (int j = 0; j < BEZIER_FRAG_SEGMENTS; ++j) { 266 | float t = (float) j / (BEZIER_FRAG_SEGMENTS - 1); 267 | ImVec2 p = ImBezierCubicCalc(points[i], control_points[i][0], control_points[i][1], points[i + 1], t); 268 | LUT_points[i * BEZIER_FRAG_SEGMENTS + j] = p; 269 | if (last_point.x > p.x) { 270 | // Bad Curve 271 | } 272 | last_point = p; 273 | } 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /gui/CustomCurve.h: -------------------------------------------------------------------------------- 1 | #ifndef CUSTOMCURVE_H 2 | #define CUSTOMCURVE_H 3 | 4 | #include "External/ImGui/imgui.h" 5 | #include 6 | #include 7 | #include 8 | 9 | #include "External/ImGui/implot.h" 10 | 11 | #define CURVE_POINTS_MARGIN 0.2f 12 | #define BEZIER_FRAG_SEGMENTS 50 13 | 14 | struct Ex_Vec2 : ImVec2 { 15 | bool is_locked = false; 16 | 17 | Ex_Vec2(float x, float y) : ImVec2(x, y) {} 18 | Ex_Vec2(ImVec2 vec) : ImVec2(vec) {} 19 | Ex_Vec2() : ImVec2(0, 0) {} 20 | }; 21 | 22 | class CustomCurve { 23 | public: 24 | std::deque points{{5, 1}, {50, 2}}; // actual points 25 | std::deque > control_points{std::array({ImVec2{40, 1}, ImVec2{20, 2}})}; 26 | std::vector LUT_points{}; 27 | 28 | CustomCurve() = default; 29 | 30 | // Constraints the curve to be aligned with "mathematical" definition of a function x -> f(x) 31 | void ApplyCurveConstraints(); 32 | 33 | // Tries to optimally distribute the points for the exported LUT 34 | int ExportCurveToLUT(double *LUT_data_x, double *LUT_data_y) const; 35 | 36 | // Makes first and second derivative continuous 37 | void SmoothBezier(); 38 | 39 | // Builds a LUT for a fast curve plotting (stored in LUT_points), no "fancy" algorithms here 40 | void UpdateLUT(); 41 | }; 42 | 43 | 44 | #endif //CUSTOMCURVE_H 45 | -------------------------------------------------------------------------------- /gui/DriverHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "DriverHelper.h" 2 | #include "External/FixedMath/Fixed64.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "External/ImGui/imgui_internal.h" 14 | #include "External/ImGui/implot.h" 15 | 16 | #define YEETMOUSE_PARAMS_DIR "/sys/module/yeetmouse/parameters/" 17 | 18 | template 19 | bool GetParameterTy(const std::string ¶m_name, Ty &value) { 20 | try { 21 | using namespace std; 22 | ifstream file(YEETMOUSE_PARAMS_DIR + param_name); 23 | 24 | if (file.bad()) 25 | return false; 26 | 27 | file >> value; 28 | file.close(); 29 | return true; 30 | } catch (std::exception &ex) { 31 | fprintf(stderr, "Error when reading parameter %s (%s)\n", param_name.c_str(), ex.what()); 32 | return false; 33 | } 34 | } 35 | 36 | bool GetParameterTy(const std::string ¶m_name, std::string &value) { 37 | try { 38 | using namespace std; 39 | ifstream file(YEETMOUSE_PARAMS_DIR + param_name); 40 | 41 | if (file.bad()) 42 | return false; 43 | 44 | std::stringstream ss; 45 | ss << file.rdbuf(); 46 | value = ss.str(); 47 | file.close(); 48 | return true; 49 | } catch (std::exception &ex) { 50 | fprintf(stderr, "Error when reading parameter %s (%s)\n", param_name.c_str(), ex.what()); 51 | return false; 52 | } 53 | } 54 | 55 | template 56 | bool SetParameterTy(const std::string ¶m_name, Ty value) { 57 | try { 58 | using namespace std; 59 | ofstream file(YEETMOUSE_PARAMS_DIR + param_name); 60 | 61 | if (file.bad()) 62 | return false; 63 | 64 | file << value; 65 | file.close(); 66 | return true; 67 | } catch (std::exception &ex) { 68 | fprintf(stderr, "Error when saving parameter %s (%s)\n", param_name.c_str(), ex.what()); 69 | return false; 70 | } 71 | } 72 | 73 | namespace DriverHelper { 74 | bool GetParameterF(const std::string ¶m_name, float &value) { 75 | return GetParameterTy(param_name, value); 76 | } 77 | 78 | bool GetParameterI(const std::string ¶m_name, int &value) { 79 | return GetParameterTy(param_name, value); 80 | } 81 | 82 | bool GetParameterB(const std::string ¶m_name, bool &value) { 83 | int temp = 0; 84 | bool res = GetParameterTy(param_name, temp); 85 | value = temp == 1; 86 | return res; 87 | } 88 | 89 | bool GetParameterS(const std::string ¶m_name, std::string &value) { 90 | return GetParameterTy(param_name, value); 91 | } 92 | 93 | bool CleanParameters(int &fixed_num) { 94 | namespace fs = std::filesystem; 95 | 96 | for (const auto &entry: fs::directory_iterator(YEETMOUSE_PARAMS_DIR)) { 97 | std::string str; 98 | std::ifstream t(entry.path()); 99 | if (!t.is_open() || t.bad() || t.fail()) 100 | return false; 101 | std::stringstream buffer; 102 | buffer << t.rdbuf(); 103 | str = buffer.str(); 104 | //printf("param at %s = %s\n", entry.path().c_str(), str.c_str()); 105 | 106 | //std::streampos size = t.tellg(); 107 | //std::cout << "pos = " << size << std::endl; 108 | //t.clear(); 109 | //t.seekp(0); 110 | // I assume this is enough to not leave behind some parts of the old values if the new ones are shorter 111 | t.close(); 112 | 113 | try { 114 | // Integer written with FP64_Shift 115 | if (size_t bracket_pos = str.find('('), ll_pos = str.find("ll"); 116 | str.find("<< 32") != std::string::npos && bracket_pos != std::string::npos && ll_pos != 117 | std::string::npos) { 118 | fixed_num++; 119 | std::ofstream o(entry.path()); 120 | if (!o.is_open() || o.bad() || o.fail()) 121 | return false; 122 | std::string int_str = str.substr(bracket_pos + 1, ll_pos - bracket_pos - 1); 123 | //printf("Clean param: %s\n", int_str.c_str()); 124 | o.write(int_str.c_str(), int_str.size()); 125 | o.close(); 126 | } else if (ll_pos != std::string::npos) { 127 | // Floating point represented as a long long 128 | fixed_num++; 129 | size_t start_offset = bracket_pos == std::string::npos ? 0 : (bracket_pos + 1); 130 | std::ofstream o(entry.path()); 131 | if (!o.is_open() || o.bad() || o.fail()) 132 | return false; 133 | std::string int_str = str.substr(start_offset, ll_pos - start_offset); 134 | FP_LONG fp_val = std::stoll(int_str); 135 | char buf[24]; 136 | FP64_ToString(fp_val, buf, 6); 137 | //printf("Clean param: %s, which is %s\n", int_str.c_str(), buf); 138 | o.write(buf, strlen(buf)); 139 | o.close(); 140 | } else { 141 | // Anything else is either 0 or not meant to be a floating point 142 | //printf("Wrong format \\;\n"); 143 | } 144 | } catch (const std::exception &ex) { 145 | fprintf(stderr, "Error parsing parameter %s!\n", entry.path().filename().c_str()); 146 | return false; 147 | } 148 | } 149 | 150 | // Save the new (clean) parameters. Nothing should change, it just looks nicer. 151 | SaveParameters(); 152 | 153 | return true; 154 | } 155 | 156 | bool SaveParameters() { 157 | return SetParameterTy("update", (int) 1); 158 | } 159 | 160 | bool WriteParameterF(const std::string ¶m_name, float value) { 161 | return SetParameterTy(param_name, value); 162 | } 163 | 164 | bool WriteParameterI(const std::string ¶m_name, float value) { 165 | return SetParameterTy(param_name, value); 166 | } 167 | 168 | bool ValidateDirectory() { 169 | namespace fs = std::filesystem; 170 | try { 171 | auto dir = fs::directory_entry(YEETMOUSE_PARAMS_DIR); 172 | if (!dir.exists()) 173 | return false; 174 | } catch (std::exception &ex) { 175 | return false; 176 | } 177 | 178 | return true; 179 | } 180 | 181 | size_t ParseUserLutData(char *szUser_data, double *out_x, double *out_y, size_t out_size) { 182 | if (!szUser_data) { 183 | strcpy(szUser_data, "Bad data pointer\0"); 184 | return 0; 185 | } 186 | 187 | std::stringstream ss(szUser_data); 188 | size_t idx = 0; 189 | 190 | // Skip 2 equal pairs (it would cause kernel to panic...) 191 | std::set visited_x; 192 | bool was_last_x_dup = false; 193 | 194 | try { 195 | double p = 0; 196 | while (idx < out_size * 2 && ss >> p) { 197 | if (idx % 2 == 1 && was_last_x_dup && out_y[(idx - 2) / 2] == p) { 198 | idx--; 199 | was_last_x_dup = false; 200 | int skipped = 0; 201 | char nextC = ss.peek(); 202 | while (nextC == ',' || nextC == ';' || (skipped > 0 && isspace(nextC))) { 203 | ss.ignore(); 204 | nextC = ss.peek(); 205 | skipped++; 206 | } 207 | continue; 208 | } 209 | 210 | was_last_x_dup = false; 211 | if (idx % 2 == 0) { 212 | if (visited_x.find(p) != visited_x.end()) 213 | was_last_x_dup = true; 214 | else 215 | visited_x.insert(p); 216 | } 217 | ((idx % 2 == 0) ? out_x : out_y)[idx++ / 2] = p; 218 | 219 | //((idx % 2 == 0) ? out_x : out_y)[idx++ / 2] = p; 220 | 221 | int skipped = 0; 222 | char nextC = ss.peek(); 223 | while (nextC == ',' || nextC == ';' || (skipped > 0 && isspace(nextC))) { 224 | ss.ignore(); 225 | nextC = ss.peek(); 226 | skipped++; 227 | } 228 | } 229 | 230 | //for(int i = 0; i < idx/2; i++) { 231 | // printf("%f, ", out_x[i]); 232 | //} 233 | //printf("\n"); 234 | 235 | // 1 element is not enough for a linear interpolation 236 | if (idx <= 2 || idx % 2 == 1) { 237 | strcpy(szUser_data, "Not enough values or bad formatting\0"); 238 | return 0; 239 | } 240 | 241 | // Make sure all the data was parsed, if not then return 0 242 | if (!ss.eof()) { 243 | sprintf(szUser_data, "Too many samples! (%zu max)", out_size); 244 | fprintf(stderr, "Too many samples! (%zu max)\n", out_size); 245 | return 0; 246 | } 247 | 248 | // Zip the X and Y values for sorting 249 | std::pair pairs[MAX_LUT_ARRAY_SIZE]; 250 | for (int i = 0; i < idx / 2; i++) 251 | pairs[i] = std::make_pair(out_x[i], out_y[i]); 252 | 253 | // Sort the values together (according to X). While preserving the ordering in case of equal X values 254 | std::sort(pairs, pairs + idx / 2, 255 | [](std::pair a, std::pair b) { return a.first < b.first; }); 256 | 257 | // Unzip 258 | for (int i = 0; i < idx / 2; i++) { 259 | if (i >= 1) { 260 | if (pairs[i].first == pairs[i - 1].first && pairs[i].second == pairs[i - 1].second) { 261 | continue; 262 | } 263 | } 264 | out_x[i] = pairs[i].first; 265 | out_y[i] = pairs[i].second; 266 | } 267 | 268 | return idx / 2; 269 | } catch (std::exception &ex) { 270 | printf("Error parsing user LUT data: %s\n", ex.what()); 271 | return 0; 272 | } 273 | } 274 | 275 | size_t ParseDriverLutData(const char *szUser_data, double *out_x, double *out_y) { 276 | std::stringstream ss(szUser_data); 277 | size_t idx = 0; 278 | 279 | double p = 0; 280 | while (idx < MAX_LUT_ARRAY_SIZE * 2 && ss >> p) { 281 | //printf("idx = %zu, p = %f\n", idx, p); 282 | (idx % 2 == 0 ? out_x : out_y)[idx++ / 2] = p; 283 | 284 | char nextC = ss.peek(); 285 | if (nextC == ';') 286 | ss.ignore(); 287 | 288 | //idx++; 289 | } 290 | 291 | // 1 element is not enough for a linear interpolation 292 | if (idx <= 2 || idx % 2 == 1) { 293 | return 0; 294 | } 295 | 296 | return idx / 2; 297 | } 298 | 299 | std::string EncodeLutData(double *data_x, double *data_y, size_t size) { 300 | std::string res; 301 | 302 | for (int i = 0; i < size * 2; i++) { 303 | res += std::to_string(i % 2 == 0 ? data_x[i / 2] : data_y[i / 2]) + ";"; 304 | } 305 | 306 | return res; 307 | } 308 | } // DriverHelper 309 | 310 | //Parameters::Parameters(float sens, float sensCap, float speedCap, float offset, float accel, float exponent, 311 | // float midpoint, float scrollAccel, int accelMode) : sens(sens), outCap(sensCap), 312 | // inCap(speedCap), offset(offset), 313 | // accel(accel), exponent(exponent), 314 | // midpoint(midpoint), scrollAccel(scrollAccel), 315 | // accelMode(accelMode) {} 316 | 317 | bool Parameters::SaveAll() { 318 | bool res = true; 319 | 320 | // General 321 | res &= SetParameterTy("Sensitivity", sens); 322 | res &= SetParameterTy("SensitivityY", use_anisotropy ? sensY : sens); 323 | res &= SetParameterTy("OutputCap", outCap); 324 | res &= SetParameterTy("InputCap", inCap); 325 | res &= SetParameterTy("Offset", offset); 326 | res &= SetParameterTy("AccelerationMode", accelMode); 327 | res &= SetParameterTy("RotationAngle", rotation * DEG2RAD); 328 | res &= SetParameterTy("AngleSnap_Threshold", as_threshold * DEG2RAD); 329 | res &= SetParameterTy("AngleSnap_Angle", as_angle * DEG2RAD); 330 | 331 | // Specific 332 | res &= SetParameterTy("Acceleration", accel); 333 | res &= SetParameterTy("Exponent", exponent); 334 | res &= SetParameterTy("Midpoint", midpoint); 335 | res &= SetParameterTy("Motivity", motivity); 336 | res &= SetParameterTy("PreScale", preScale); 337 | res &= SetParameterTy("UseSmoothing", useSmoothing); 338 | 339 | // LUT 340 | auto encodedLutData = DriverHelper::EncodeLutData(LUT_data_x, LUT_data_y, LUT_size); 341 | if (!encodedLutData.empty()) { 342 | res &= SetParameterTy("LutSize", LUT_size); 343 | //res &= SetParameterTy("LutStride", LUT_stride); 344 | //printf("encoded: %s, size: %zu, stride: %i\n", encoded.c_str(), LUT_size, LUT_stride); 345 | res &= SetParameterTy("LutDataBuf", encodedLutData); 346 | } else if (accelMode == AccelMode_Lut) 347 | return false; 348 | 349 | if (res) 350 | res &= DriverHelper::SaveParameters(); 351 | 352 | return res; 353 | } 354 | -------------------------------------------------------------------------------- /gui/DriverHelper.h: -------------------------------------------------------------------------------- 1 | #ifndef YEETMOUSE_DRIVERHELPER_H 2 | #define YEETMOUSE_DRIVERHELPER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "CustomCurve.h" 10 | #include "../shared_definitions.h" 11 | 12 | #define MAX_LUT_ARRAY_SIZE 128 // THIS NEEDS TO BE THE SAME AS IN THE DRIVER CODE 13 | 14 | #define DEG2RAD (M_PI / 180.0) 15 | 16 | namespace DriverHelper { 17 | bool GetParameterF(const std::string ¶m_name, float &value); 18 | bool GetParameterI(const std::string ¶m_name, int &value); 19 | bool GetParameterB(const std::string ¶m_name, bool &value); 20 | bool GetParameterS(const std::string ¶m_name, std::string &value); 21 | 22 | bool WriteParameterF(const std::string ¶m_name, float value); 23 | bool WriteParameterI(const std::string ¶m_name, float value); 24 | 25 | bool SaveParameters(); 26 | 27 | bool ValidateDirectory(); 28 | 29 | /// Converts the ugly FP64 representation of user parameters to nice floating point values 30 | bool CleanParameters(int &fixed_num); 31 | 32 | /// Returns the number of parsed values 33 | size_t ParseUserLutData(char *user_data, double *out_x, double *out_y, size_t out_size); 34 | 35 | /// Returns the number of parsed values 36 | size_t ParseDriverLutData(const char *user_data, double *out_x, double *out_y); 37 | 38 | std::string EncodeLutData(double *data_x, double *data_y, size_t size); 39 | } // DriverHelper 40 | 41 | inline std::string AccelMode2String(AccelMode mode) { 42 | static_assert(AccelMode_Count == 10); 43 | 44 | switch (mode) { 45 | case AccelMode_Current: 46 | return "Current"; 47 | case AccelMode_Linear: 48 | return "Linear"; 49 | case AccelMode_Power: 50 | return "Power"; 51 | case AccelMode_Classic: 52 | return "Classic"; 53 | case AccelMode_Motivity: 54 | return "Motivity"; 55 | case AccelMode_Synchronous: 56 | return "Synchronous"; 57 | case AccelMode_Natural: 58 | return "Natural"; 59 | case AccelMode_Jump: 60 | return "Jump"; 61 | case AccelMode_Lut: 62 | return "LUT"; 63 | default: 64 | return "Unknown"; 65 | } 66 | } 67 | 68 | inline std::string AccelMode2EnumString(AccelMode mode) { 69 | static_assert(AccelMode_Count == 10); 70 | 71 | switch (mode) { 72 | case AccelMode_Current: 73 | return "AccelMode_Current"; 74 | case AccelMode_Linear: 75 | return "AccelMode_Linear"; 76 | case AccelMode_Power: 77 | return "AccelMode_Power"; 78 | case AccelMode_Classic: 79 | return "AccelMode_Classic"; 80 | case AccelMode_Motivity: 81 | return "AccelMode_Motivity"; 82 | case AccelMode_Synchronous: 83 | return "AccelMode_Synchronous"; 84 | case AccelMode_Natural: 85 | return "AccelMode_Natural"; 86 | case AccelMode_Jump: 87 | return "AccelMode_Jump"; 88 | case AccelMode_Lut: 89 | return "AccelMode_Lut"; 90 | default: 91 | return "AccelMode_Current"; 92 | } 93 | } 94 | 95 | inline std::string AccelMode2String_CAPS(AccelMode mode) { 96 | static_assert(AccelMode_Count == 10); 97 | 98 | switch (mode) { 99 | case AccelMode_Current: 100 | return "CURRENT"; 101 | case AccelMode_Linear: 102 | return "LINEAR"; 103 | case AccelMode_Power: 104 | return "POWER"; 105 | case AccelMode_Classic: 106 | return "CLASSIC"; 107 | case AccelMode_Motivity: 108 | return "MOTIVITY"; 109 | case AccelMode_Natural: 110 | return "NATURAL"; 111 | case AccelMode_Synchronous: 112 | return "SYNCHRONOUS"; 113 | case AccelMode_Jump: 114 | return "JUMP"; 115 | case AccelMode_Lut: 116 | return "LUT"; 117 | default: 118 | return "Unknown"; 119 | } 120 | } 121 | 122 | inline AccelMode AccelMode_From_String(std::string mode_text) { 123 | static_assert(AccelMode_Count == 10); 124 | 125 | // Bring text to lowercase 126 | std::transform(mode_text.begin(), mode_text.end(), mode_text.begin(), 127 | [](unsigned char c) { return std::tolower(c); }); 128 | 129 | if (mode_text == "current") 130 | return AccelMode_Current; 131 | if (mode_text == "linear") 132 | return AccelMode_Linear; 133 | if (mode_text == "power") 134 | return AccelMode_Power; 135 | if (mode_text == "classic") 136 | return AccelMode_Classic; 137 | if (mode_text == "motivity") 138 | return AccelMode_Motivity; 139 | if (mode_text == "synchronous") 140 | return AccelMode_Synchronous; 141 | if (mode_text == "natural") 142 | return AccelMode_Natural; 143 | if (mode_text == "jump") 144 | return AccelMode_Jump; 145 | if (mode_text == "lut") 146 | return AccelMode_Lut; 147 | if (mode_text == "custom curve") 148 | return AccelMode_CustomCurve; 149 | return AccelMode_Current; 150 | } 151 | 152 | inline AccelMode AccelMode_From_EnumString(const std::string &mode_text) { 153 | static_assert(AccelMode_Count == 10); 154 | 155 | if (mode_text == "AccelMode_Current") 156 | return AccelMode_Current; 157 | if (mode_text == "AccelMode_Linear") 158 | return AccelMode_Linear; 159 | if (mode_text == "AccelMode_Power") 160 | return AccelMode_Power; 161 | if (mode_text == "AccelMode_Classic") 162 | return AccelMode_Classic; 163 | if (mode_text == "AccelMode_Motivity") 164 | return AccelMode_Motivity; 165 | if (mode_text == "AccelMode_Synchronous") 166 | return AccelMode_Synchronous; 167 | if (mode_text == "AccelMode_Natural") 168 | return AccelMode_Natural; 169 | if (mode_text == "AccelMode_Jump") 170 | return AccelMode_Jump; 171 | if (mode_text == "AccelMode_Lut") 172 | return AccelMode_Lut; 173 | if (mode_text == "AccelMode_CustomCurve") 174 | return AccelMode_CustomCurve; 175 | } 176 | 177 | struct Parameters { 178 | float sens = 1.0f; 179 | // Sensitivity for X axis only if sens != sensY (anisotropy is on), otherwise sensitivity for both axes 180 | float sensY = 1.0f; // Unused when anisotropy is off (sens == sensY) 181 | float outCap = 0.f; 182 | float inCap = 0.f; 183 | float offset = 0.0f; 184 | float preScale = 1.0f; 185 | float accel = 2.0f; 186 | float exponent = 0.4f; 187 | float midpoint = 5.0f; 188 | float motivity = 1.5f; 189 | //float scrollAccel = 1.0f; 190 | AccelMode accelMode = AccelMode_Current; 191 | bool useSmoothing = true; // true/false 192 | float rotation = 0; // Stored in degrees, converted to radians when writing out 193 | float as_threshold = 0; // Stored in degrees, converted to radians when writing out 194 | float as_angle = 0; // Stored in degrees, converted to radians when writing out 195 | 196 | /// The issue of performance with LUT is currently solved with a fixed stride, but another approach would be to 197 | /// store separately x and y values, sort both by x values, and do a binary search every time you want to find points. 198 | /// ----- The so called "another approach" has been implemented ----- 199 | //float LUT_stride = 1.f; // No longer used 200 | double LUT_data_x[MAX_LUT_ARRAY_SIZE]; 201 | double LUT_data_y[MAX_LUT_ARRAY_SIZE]; 202 | int LUT_size = 0; 203 | 204 | CustomCurve customCurve{}; 205 | 206 | Parameters() = default; 207 | 208 | bool use_anisotropy = false; // This parameter is not saved anywhere, it's just a helper. 209 | // Anisotropy is on if sensY != sens, off otherwise. 210 | 211 | //Parameters(float sens, float sensCap, float speedCap, float offset, float accel, float exponent, float midpoint, 212 | // float scrollAccel, int accelMode); 213 | 214 | bool SaveAll(); 215 | }; 216 | 217 | #endif //YEETMOUSE_DRIVERHELPER_H 218 | -------------------------------------------------------------------------------- /gui/External/ImGui/imconfig.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // DEAR IMGUI COMPILE-TIME OPTIONS 3 | // Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure. 4 | // You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions. 5 | //----------------------------------------------------------------------------- 6 | // A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it) 7 | // B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template. 8 | //----------------------------------------------------------------------------- 9 | // You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp 10 | // files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures. 11 | // Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts. 12 | // Call IMGUI_CHECKVERSION() from your .cpp file to verify that the data structures your files are using are matching the ones imgui.cpp is using. 13 | //----------------------------------------------------------------------------- 14 | 15 | #pragma once 16 | 17 | //---- Define assertion handler. Defaults to calling assert(). 18 | // If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement. 19 | //#define IM_ASSERT(_EXPR) MyAssert(_EXPR) 20 | //#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts 21 | 22 | //---- Define attributes of all API symbols declarations, e.g. for DLL under Windows 23 | // Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility. 24 | // - Windows DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions() 25 | // for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details. 26 | //#define IMGUI_API __declspec(dllexport) // MSVC Windows: DLL export 27 | //#define IMGUI_API __declspec(dllimport) // MSVC Windows: DLL import 28 | //#define IMGUI_API __attribute__((visibility("default"))) // GCC/Clang: override visibility when set is hidden 29 | 30 | //---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names. 31 | //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS 32 | //#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87+ disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This is automatically done by IMGUI_DISABLE_OBSOLETE_FUNCTIONS. 33 | 34 | //---- Disable all of Dear ImGui or don't implement standard windows/tools. 35 | // It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp. 36 | //#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty. 37 | //#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. 38 | //#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowIDStackToolWindow() will be empty. 39 | 40 | //---- Don't implement some functions to reduce linkage requirements. 41 | //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a) 42 | //#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW) 43 | //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a) 44 | //#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME). 45 | //#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default). 46 | //#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default io.PlatformOpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")). 47 | //#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf) 48 | //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. 49 | //#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies) 50 | //#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. 51 | //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). 52 | //#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available 53 | 54 | //---- Enable Test Engine / Automation features. 55 | //#define IMGUI_ENABLE_TEST_ENGINE // Enable imgui_test_engine hooks. Generally set automatically by include "imgui_te_config.h", see Test Engine for details. 56 | 57 | //---- Include imgui_user.h at the end of imgui.h as a convenience 58 | // May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included. 59 | //#define IMGUI_INCLUDE_IMGUI_USER_H 60 | //#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h" 61 | 62 | //---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another) 63 | //#define IMGUI_USE_BGRA_PACKED_COLOR 64 | 65 | //---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...) 66 | //#define IMGUI_USE_WCHAR32 67 | 68 | //---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version 69 | // By default the embedded implementations are declared static and not available outside of Dear ImGui sources files. 70 | //#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" 71 | //#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" 72 | //#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if IMGUI_USE_STB_SPRINTF is defined. 73 | //#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION 74 | //#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION 75 | //#define IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION // only disabled if IMGUI_USE_STB_SPRINTF is defined. 76 | 77 | //---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined) 78 | // Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h. 79 | //#define IMGUI_USE_STB_SPRINTF 80 | 81 | //---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui) 82 | // Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided). 83 | // On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'. 84 | //#define IMGUI_ENABLE_FREETYPE 85 | 86 | //---- Use FreeType+lunasvg library to render OpenType SVG fonts (SVGinOT) 87 | // Requires lunasvg headers to be available in the include path + program to be linked with the lunasvg library (not provided). 88 | // Only works in combination with IMGUI_ENABLE_FREETYPE. 89 | // (implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement) 90 | //#define IMGUI_ENABLE_FREETYPE_LUNASVG 91 | 92 | //---- Use stb_truetype to build and rasterize the font atlas (default) 93 | // The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend. 94 | //#define IMGUI_ENABLE_STB_TRUETYPE 95 | 96 | //---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4. 97 | // This will be inlined as part of ImVec2 and ImVec4 class declarations. 98 | 99 | /* 100 | #define IM_VEC2_CLASS_EXTRA \ 101 | constexpr ImPlotPoint(const ImPlotPoint& f) : x(f.x), y(f.y) {} \ 102 | operator ImPlotPoint() const { return ImPlotPoint(x,y); } 103 | 104 | #define IM_VEC4_CLASS_EXTRA \ 105 | constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {} \ 106 | operator MyVec4() const { return MyVec4(x,y,z,w); } 107 | */ 108 | //---- ...Or use Dear ImGui's own very basic math operators. 109 | #define IMGUI_DEFINE_MATH_OPERATORS 110 | 111 | #define IMPLOT_POINT_CLASS_EXTRA \ 112 | operator ImVec2() const { return ImVec2(x, y); } 113 | 114 | //---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices. 115 | // Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices). 116 | // Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer. 117 | // Read about ImGuiBackendFlags_RendererHasVtxOffset for details. 118 | //#define ImDrawIdx unsigned int 119 | 120 | //---- Override ImDrawCallback signature (will need to modify renderer backends accordingly) 121 | //struct ImDrawList; 122 | //struct ImDrawCmd; 123 | //typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data); 124 | //#define ImDrawCallback MyImDrawCallback 125 | 126 | //---- Debug Tools: Macro to break in Debugger (we provide a default implementation of this in the codebase) 127 | // (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.) 128 | //#define IM_DEBUG_BREAK IM_ASSERT(0) 129 | //#define IM_DEBUG_BREAK __debugbreak() 130 | 131 | //---- Debug Tools: Enable slower asserts 132 | //#define IMGUI_DEBUG_PARANOID 133 | 134 | //---- Tip: You can add extra functions within the ImGui:: namespace from anywhere (e.g. your own sources/header files) 135 | /* 136 | namespace ImGui 137 | { 138 | void MyFunction(const char* name, MyMatrix44* mtx); 139 | } 140 | */ 141 | -------------------------------------------------------------------------------- /gui/External/ImGui/imgui_impl_glfw.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Platform Backend for GLFW 2 | // This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..) 3 | // (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) 4 | 5 | // Implemented features: 6 | // [X] Platform: Clipboard support. 7 | // [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen (Windows only). 8 | // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] 9 | // [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. 10 | // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+). 11 | 12 | // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 13 | // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. 14 | // Learn about Dear ImGui: 15 | // - FAQ https://dearimgui.com/faq 16 | // - Getting Started https://dearimgui.com/getting-started 17 | // - Documentation https://dearimgui.com/docs (same as your local docs/ folder). 18 | // - Introduction, links and more at the top of imgui.cpp 19 | 20 | #pragma once 21 | #include "imgui.h" // IMGUI_IMPL_API 22 | #ifndef IMGUI_DISABLE 23 | 24 | struct GLFWwindow; 25 | struct GLFWmonitor; 26 | 27 | // Follow "Getting Started" link and check examples/ folder to learn about using backends! 28 | IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks); 29 | IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks); 30 | IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks); 31 | IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown(); 32 | IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame(); 33 | 34 | // Emscripten related initialization phase methods (call after ImGui_ImplGlfw_InitForOpenGL) 35 | #ifdef __EMSCRIPTEN__ 36 | IMGUI_IMPL_API void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow* window, const char* canvas_selector); 37 | //static inline void ImGui_ImplGlfw_InstallEmscriptenCanvasResizeCallback(const char* canvas_selector) { ImGui_ImplGlfw_InstallEmscriptenCallbacks(nullptr, canvas_selector); } } // Renamed in 1.91.0 38 | #endif 39 | 40 | // GLFW callbacks install 41 | // - When calling Init with 'install_callbacks=true': ImGui_ImplGlfw_InstallCallbacks() is called. GLFW callbacks will be installed for you. They will chain-call user's previously installed callbacks, if any. 42 | // - When calling Init with 'install_callbacks=false': GLFW callbacks won't be installed. You will need to call individual function yourself from your own GLFW callbacks. 43 | IMGUI_IMPL_API void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window); 44 | IMGUI_IMPL_API void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window); 45 | 46 | // GFLW callbacks options: 47 | // - Set 'chain_for_all_windows=true' to enable chaining callbacks for all windows (including secondary viewports created by backends or by user) 48 | IMGUI_IMPL_API void ImGui_ImplGlfw_SetCallbacksChainForAllWindows(bool chain_for_all_windows); 49 | 50 | // GLFW callbacks (individual callbacks to call yourself if you didn't install callbacks) 51 | IMGUI_IMPL_API void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused); // Since 1.84 52 | IMGUI_IMPL_API void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered); // Since 1.84 53 | IMGUI_IMPL_API void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y); // Since 1.87 54 | IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); 55 | IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); 56 | IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); 57 | IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c); 58 | IMGUI_IMPL_API void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor* monitor, int event); 59 | 60 | #endif // #ifndef IMGUI_DISABLE 61 | -------------------------------------------------------------------------------- /gui/External/ImGui/imgui_impl_opengl3.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline 2 | // - Desktop GL: 2.x 3.x 4.x 3 | // - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0) 4 | // This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) 5 | 6 | // Implemented features: 7 | // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID! 8 | // [x] Renderer: Large meshes support (64k+ vertices) with 16-bit indices (Desktop OpenGL only). 9 | 10 | // About WebGL/ES: 11 | // - You need to '#define IMGUI_IMPL_OPENGL_ES2' or '#define IMGUI_IMPL_OPENGL_ES3' to use WebGL or OpenGL ES. 12 | // - This is done automatically on iOS, Android and Emscripten targets. 13 | // - For other targets, the define needs to be visible from the imgui_impl_opengl3.cpp compilation unit. If unsure, define globally or in imconfig.h. 14 | 15 | // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 16 | // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. 17 | // Learn about Dear ImGui: 18 | // - FAQ https://dearimgui.com/faq 19 | // - Getting Started https://dearimgui.com/getting-started 20 | // - Documentation https://dearimgui.com/docs (same as your local docs/ folder). 21 | // - Introduction, links and more at the top of imgui.cpp 22 | 23 | // About GLSL version: 24 | // The 'glsl_version' initialization parameter should be nullptr (default) or a "#version XXX" string. 25 | // On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es" 26 | // Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp. 27 | 28 | #pragma once 29 | #include "imgui.h" // IMGUI_IMPL_API 30 | #ifndef IMGUI_DISABLE 31 | 32 | // Follow "Getting Started" link and check examples/ folder to learn about using backends! 33 | IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = nullptr); 34 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown(); 35 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame(); 36 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data); 37 | 38 | // (Optional) Called by Init/NewFrame/Shutdown 39 | IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture(); 40 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture(); 41 | IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects(); 42 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects(); 43 | 44 | // Configuration flags to add in your imconfig file: 45 | //#define IMGUI_IMPL_OPENGL_ES2 // Enable ES 2 (Auto-detected on Emscripten) 46 | //#define IMGUI_IMPL_OPENGL_ES3 // Enable ES 3 (Auto-detected on iOS/Android) 47 | 48 | // You can explicitly select GLES2 or GLES3 API by using one of the '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line. 49 | #if !defined(IMGUI_IMPL_OPENGL_ES2) \ 50 | && !defined(IMGUI_IMPL_OPENGL_ES3) 51 | 52 | // Try to detect GLES on matching platforms 53 | #if defined(__APPLE__) 54 | #include 55 | #endif 56 | #if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__)) 57 | #define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es" 58 | #elif defined(__EMSCRIPTEN__) || defined(__amigaos4__) 59 | #define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100" 60 | #else 61 | // Otherwise imgui_impl_opengl3_loader.h will be used. 62 | #endif 63 | 64 | #endif 65 | 66 | #endif // #ifndef IMGUI_DISABLE 67 | -------------------------------------------------------------------------------- /gui/FunctionHelper.h: -------------------------------------------------------------------------------- 1 | #ifndef GUI_FUNCTIONHELPER_H 2 | #define GUI_FUNCTIONHELPER_H 3 | 4 | 5 | #include "DriverHelper.h" 6 | 7 | #define PLOT_POINTS (512) 8 | #define PLOT_X_RANGE (150) 9 | 10 | #define LERP(a,b,x) (((b) - (a)) * (x) + (a)) 11 | 12 | class CachedFunction { 13 | public: 14 | float values[PLOT_POINTS]{0}; 15 | float values_y[PLOT_POINTS]{0}; 16 | float x_stride = 0; 17 | Parameters *params; 18 | 19 | bool isValid = true; 20 | 21 | CachedFunction(float xStride, Parameters *params); 22 | 23 | CachedFunction() { 24 | }; 25 | 26 | float EvalFuncAt(float x); 27 | 28 | void PreCacheConstants(); 29 | 30 | void PreCacheFunc(); // Also validates settings 31 | 32 | // Result also saved into 'bool isValid' 33 | bool ValidateSettings(); 34 | 35 | private: 36 | // Constant parameters for Jump 37 | double smoothness = 0; 38 | double C0 = 0; 39 | 40 | // Power 41 | float offset_x = 0; 42 | float power_constant = 0; 43 | 44 | // Synchronous 45 | struct SynchronousData { 46 | static constexpr int start = -3; 47 | static constexpr int stop = 9; 48 | static constexpr int num = 8; 49 | static constexpr int capacity = (stop - start) * num + 1; 50 | static constexpr bool velocity = true; 51 | 52 | std::vector data; 53 | double xStart; 54 | } synchronous_data; 55 | 56 | bool SynchronousBuildLUT(); 57 | 58 | float SynchronousLegacy(float x) const; 59 | 60 | float SynchronousGainEval(float x) const; 61 | }; 62 | 63 | #endif //GUI_FUNCTIONHELPER_H 64 | -------------------------------------------------------------------------------- /gui/ImGuiExtensions.cpp: -------------------------------------------------------------------------------- 1 | #include "ImGuiExtensions.h" 2 | 3 | bool ImGui::ModeSelectable(const char *label, bool is_selected, ImGuiSelectableFlags flags, const ImVec2 &size) { 4 | ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(20, 24)); 5 | ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.5); 6 | if (!is_selected) 7 | ImGui::PushStyleColor(ImGuiCol_Button, {0, 0, 0, 0}); 8 | else 9 | ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg)); 10 | 11 | bool ret = false; 12 | 13 | if (ImGui::Button(label, size)) { 14 | ret = true; 15 | } 16 | 17 | ImGui::PopStyleVar(2); 18 | ImGui::PopStyleColor(); 19 | 20 | return ret; 21 | } 22 | 23 | bool ImGui::ParameterSlider(const char *label, float &value, float v_speed, float v_min, float v_max) { 24 | //ImGui::Text() 25 | } 26 | -------------------------------------------------------------------------------- /gui/ImGuiExtensions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef YEETMOUSE_IMGUIEXTENSIONS_H 4 | #define YEETMOUSE_IMGUIEXTENSIONS_H 5 | 6 | #include "External/ImGui/imgui.h" 7 | 8 | namespace ImGui { 9 | bool ModeSelectable(const char *label, bool is_selected = false, ImGuiSelectableFlags flags = 0, 10 | const ImVec2 &size = ImVec2(0, 0)); 11 | 12 | bool ParameterSlider(const char *label, float &value, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f); 13 | } 14 | 15 | #endif // YEETMOUSE_IMGUIEXTENSIONS_H 16 | -------------------------------------------------------------------------------- /gui/Makefile: -------------------------------------------------------------------------------- 1 | # Define the compiler and flags 2 | CXX = g++ 3 | CXXFLAGS = -Wall -std=c++17 -O2 -I gui/External -flto=auto 4 | LIBS = -lglfw -ldl # this might have to be lglfw3 instead 5 | #LDFLAGS = -flto 6 | 7 | ifneq ("$(wildcard $(/usr/lib/x86_64-linux-gnu/libglfw3.so))","") # Check if glfw3 exists 8 | LIBS = -lglfw3 # and use it instead 9 | endif 10 | 11 | # glfw and OpenGL are required to compile! 12 | #sudo apt install libglfw3-dev 13 | 14 | # If I even decide to switch to libusb: 15 | #sudo apt-get install libusb-1.0-0-dev 16 | 17 | # Define the source files 18 | SOURCES = main.cpp gui.cpp DriverHelper.cpp ImGuiExtensions.cpp FunctionHelper.cpp CustomCurve.cpp ConfigHelper.cpp $(wildcard External/ImGui/*.cpp) $(wildcard gui/lib/*.cpp) 19 | 20 | # Define the object files 21 | OBJECTS = $(patsubst %.cpp, %.o, $(SOURCES)) 22 | 23 | # Define the target 24 | TARGET = YeetMouseGui 25 | 26 | default: all 27 | 28 | all: $(TARGET) 29 | 30 | # Rule to build the target 31 | $(TARGET): $(OBJECTS) 32 | $(CXX) $(CXXFLAGS) -o $@ $(OBJECTS) -L/usr/lib/x86_64-linux-gnu -lGL $(LIBS) 33 | 34 | # Rule to build the object files with LTO 35 | %.o: %.cpp 36 | $(CXX) $(CXXFLAGS) -c $< -o $@ 37 | 38 | # Rule to build the object files with LTO (only for ImGui source files) 39 | External/ImGui/%.o: External/ImGui/%.cpp 40 | $(CXX) $(CXXFLAGS) -c $< -o $@ 41 | 42 | 43 | .PHONY: clean clean-all 44 | 45 | clean-all: clean 46 | rm -rf External/ImGui/*.o 47 | 48 | # Removing the imgui object files is kinda pointless for most cases and it takes a lot of time to rebuild 49 | clean: 50 | rm -rf *.o 51 | rm $(TARGET) 52 | -------------------------------------------------------------------------------- /gui/gui.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "External/ImGui/imgui.h" 3 | #include "External/ImGui/imgui_impl_glfw.h" 4 | #include "External/ImGui/imgui_impl_opengl3.h" 5 | 6 | namespace GUI 7 | { 8 | int Setup(int (*OnGui)()); 9 | int RenderFrame(); 10 | void ShutDown(); 11 | 12 | void SetWindowSize(int x, int y); 13 | 14 | inline GLFWwindow* window; 15 | 16 | void GetMousePos(double *x, double *y); 17 | } 18 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Get the installed version of the driver 4 | installed_version=$(dkms status -k $(uname -r) | grep -oP '^([l|y]eetmouse-driver[\/(, )]) ?\K([0-9.]+)') 5 | 6 | if [[ ! -z "$installed_version" ]]; then 7 | echo "Driver ($installed_version) already installed, exiting." 8 | exit 0 9 | fi 10 | 11 | # Try to fix config.h saved in old format (acceleration mode number) 12 | CONFIG_FILE="driver/config.h" 13 | ENUM_FILE="shared_definitions.h" 14 | 15 | # Extract the number from the config file 16 | mode_number=$(grep -Po '^#define\s+ACCELERATION_MODE\s+\K\d+' "$CONFIG_FILE") 17 | 18 | if [[ "$mode_number" ]]; then 19 | 20 | echo "Fixing old version of config..." 21 | 22 | old_mode_numer=$mode_number 23 | 24 | if [ $mode_number -gt 4 ]; then 25 | mode_number=$((mode_number+2)) 26 | fi 27 | 28 | # Find matching enum entry from shared_definitions.h 29 | mode_name=$(grep -Po "AccelMode_[A-Za-z0-9_]+\s*=\s*$mode_number\b" "$ENUM_FILE" | awk -F= '{print $1}' | tr -d '[:space:]') 30 | 31 | if [[ -z "$mode_name" ]]; then 32 | echo "Could not find enum entry for value $mode_number in $ENUM_FILE" 33 | exit 1 34 | fi 35 | 36 | # Replace number in config.h with the enum name 37 | sudo sed -i -E "s|(#define\s+ACCELERATION_MODE\s+)$old_mode_numer|\1$mode_name|" "$CONFIG_FILE" 38 | 39 | echo "Done! Replaced ACCELERATION_MODE $old_mode_numer with $mode_name in $CONFIG_FILE" 40 | 41 | fi 42 | 43 | if ! grep -q MOTIVITY "$CONFIG_FILE"; then 44 | # Add missing Motivity parameter 45 | sudo sed -i -E '/^#define\s+MIDPOINT\s+/a #define MOTIVITY 1.5' "$CONFIG_FILE" 46 | fi 47 | 48 | # Install the driver and activate the dkms module 49 | sudo make setup_dkms 50 | sudo dkms install -m yeetmouse-driver -v 0.9.2 # Enter the version you determined from the Makefile earlier in here 51 | sudo modprobe yeetmouse 52 | -------------------------------------------------------------------------------- /install_files/dkms/dkms.conf: -------------------------------------------------------------------------------- 1 | PACKAGE_NAME="yeetmouse" 2 | PACKAGE_VERSION="0.9.2" 3 | AUTOINSTALL="yes" 4 | MAKE="KERNELDIR=/lib/modules/${kernelver}/build make driver" 5 | 6 | BUILT_MODULE_NAME="yeetmouse" 7 | 8 | BUILT_MODULE_LOCATION="driver" 9 | 10 | DEST_MODULE_LOCATION="/kernel/drivers/hid" 11 | -------------------------------------------------------------------------------- /media/Conversion_Error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Conversion_Error.png -------------------------------------------------------------------------------- /media/Division_-Normal,_128Bit,_Big-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Division_-Normal,_128Bit,_Big-.png -------------------------------------------------------------------------------- /media/Division_-Precise,_128Bit,_Big-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Division_-Precise,_128Bit,_Big-.png -------------------------------------------------------------------------------- /media/Division_-Precise,_128Bit,_Small-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Division_-Precise,_128Bit,_Small-.png -------------------------------------------------------------------------------- /media/Division_-Precise,_64Bit,_Big-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Division_-Precise,_64Bit,_Big-.png -------------------------------------------------------------------------------- /media/Division_-Precise,_64Bit,_Small-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Division_-Precise,_64Bit,_Small-.png -------------------------------------------------------------------------------- /media/Exponent_-Fast,_Big-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Exponent_-Fast,_Big-.png -------------------------------------------------------------------------------- /media/Exponent_-Fast,_Small-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Exponent_-Fast,_Small-.png -------------------------------------------------------------------------------- /media/Exponent_-Precise,_Big-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Exponent_-Precise,_Big-.png -------------------------------------------------------------------------------- /media/Exponent_-Precise,_Small-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Exponent_-Precise,_Small-.png -------------------------------------------------------------------------------- /media/Exponent_Comparison_-Big-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Exponent_Comparison_-Big-.png -------------------------------------------------------------------------------- /media/Exponent_Comparison_-Small-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Exponent_Comparison_-Small-.png -------------------------------------------------------------------------------- /media/InstructionPerformance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/InstructionPerformance.png -------------------------------------------------------------------------------- /media/Multiplication_-128Bit,_Big-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Multiplication_-128Bit,_Big-.png -------------------------------------------------------------------------------- /media/Multiplication_-128Bit,_Small-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Multiplication_-128Bit,_Small-.png -------------------------------------------------------------------------------- /media/Natural_Logarithm_-Fast,_Big-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Natural_Logarithm_-Fast,_Big-.png -------------------------------------------------------------------------------- /media/Natural_Logarithm_-Fast,_Small-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Natural_Logarithm_-Fast,_Small-.png -------------------------------------------------------------------------------- /media/Natural_Logarithm_-Precise,_Big-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Natural_Logarithm_-Precise,_Big-.png -------------------------------------------------------------------------------- /media/Natural_Logarithm_-Precise,_Small-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Natural_Logarithm_-Precise,_Small-.png -------------------------------------------------------------------------------- /media/Natural_Logarithm_Comparison_-Big-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Natural_Logarithm_Comparison_-Big-.png -------------------------------------------------------------------------------- /media/Natural_Logarithm_Comparison_-Small-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Natural_Logarithm_Comparison_-Small-.png -------------------------------------------------------------------------------- /media/Power_-Fast,_Big-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Power_-Fast,_Big-.png -------------------------------------------------------------------------------- /media/Power_-Fast,_Small-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Power_-Fast,_Small-.png -------------------------------------------------------------------------------- /media/Power_-Precise,_Big-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Power_-Precise,_Big-.png -------------------------------------------------------------------------------- /media/Power_-Precise,_Small-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Power_-Precise,_Small-.png -------------------------------------------------------------------------------- /media/Square_Root_-Fast,_Big-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Square_Root_-Fast,_Big-.png -------------------------------------------------------------------------------- /media/Square_Root_-Fast,_Small-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Square_Root_-Fast,_Small-.png -------------------------------------------------------------------------------- /media/Square_Root_-Precise,_Big-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Square_Root_-Precise,_Big-.png -------------------------------------------------------------------------------- /media/Square_Root_-Precise,_Small-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Square_Root_-Precise,_Small-.png -------------------------------------------------------------------------------- /media/Square_Root_Comparison_-Big-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Square_Root_Comparison_-Big-.png -------------------------------------------------------------------------------- /media/Square_Root_Comparison_-Small-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/Square_Root_Comparison_-Small-.png -------------------------------------------------------------------------------- /media/YeetMouseExportSaveConfig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/YeetMouseExportSaveConfig.png -------------------------------------------------------------------------------- /media/YeetMouseGithub.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndyFilter/YeetMouse/0d11d02199b6ff11d603742892eb0b1d8927b51b/media/YeetMouseGithub.gif -------------------------------------------------------------------------------- /nix/README.md: -------------------------------------------------------------------------------- 1 | # YeetMouse NixOS Flake 2 | 3 | YeetMouse may be built using this folder's packaged Nix flake. 4 | As the `flake.nix` file is in the `nix/` folder the flake is accessible under the `github:AndyFilter/YeetMouse?dir=nix` URL. 5 | 6 | ## NixOS Module Installation 7 | 8 | You may install this flake by adding it to your NixOS configuration. 9 | The following example shows the `inputs.yeetmouse` input and the 10 | `yeetmouse.nixosModules.default` NixOS module being added to a system 11 | in your `flake.nix` file. 12 | 13 | ```nix 14 | { 15 | # Add this to your flake inputs 16 | # Note that `inputs.nixpkgs` assumes that you have an input called 17 | # `nixpkgs` and you might need to change it based on your `nixpkgs` 18 | # input's name. 19 | inputs.yeetmouse = { 20 | url = "github:AndyFilter/YeetMouse?dir=nix"; 21 | inputs.nixpkgs.follows = "nixpkgs"; 22 | }; 23 | # ... 24 | 25 | outputs = { nixpkgs, yeetmouse, ... }: { 26 | # This is an example of a NixOS system configuration 27 | nixosConfigurations.HOSTNAME = nixpkgs.lib.nixosSystem { 28 | system = "x86_64-linux"; 29 | modules = [ 30 | # Add the `yeetmouse` input's NixOS Module to your system's modules: 31 | yeetmouse.nixosModules.default 32 | ]; 33 | }; 34 | }; 35 | } 36 | ``` 37 | 38 | This will expose a new `hardware.yeetmouse` configuration option in your NixOS system's `config`, 39 | which you can use to activate `yeetmouse`'s device driver, udev rules, and executable. 40 | 41 | ```nix 42 | { 43 | hardware.yeetmouse = { 44 | enable = true; 45 | sensitivity = 1.0; 46 | }; 47 | } 48 | ``` 49 | 50 | Then, rebuild and switch into your new system (using `nixos-rebuild`). After a reboot, the 51 | `yeetmouse` driver should be loaded for your connected mouse with the parameters you specified. 52 | 53 | After restarting your system, to verify that the driver works start up the yeetmouse GUI: 54 | 55 | ```sh 56 | sudo -E yeetmouse 57 | ``` 58 | 59 | ## Manual Overlay Installation 60 | 61 | Instead of adding the entire NixOS module, you may also manually add `pkgs.yeetmouse` as an 62 | overlay to your system. 63 | 64 | The following example also adds the `inputs.yeetmouse` input, but instad of addding the NixOS module 65 | it adds the `yeetmouse.overlays.default` overlay. 66 | 67 | ```nix 68 | { 69 | # Add this to your flake inputs 70 | # Note that `inputs.nixpkgs` assumes that you have an input called 71 | # `nixpkgs` and you might need to change it based on your `nixpkgs` 72 | # input's name. 73 | inputs.yeetmouse = { 74 | url = "github:AndyFilter/YeetMouse?dir=nix"; 75 | inputs.nixpkgs.follows = "nixpkgs"; 76 | }; 77 | # ... 78 | 79 | outputs = { nixpkgs, yeetmouse, ... }: { 80 | # This is an example of a NixOS system configuration 81 | nixosConfigurations.HOSTNAME = nixpkgs.lib.nixosSystem { 82 | system = "x86_64-linux"; 83 | overlays = [ 84 | # Add the `yeetmouse` input's overlay to your system's overlays: 85 | yeetmouse.overlays.default 86 | ]; 87 | }; 88 | }; 89 | } 90 | ``` 91 | 92 | This will only add a `pkgs.yeetmouse` package to your NixOS system configuration, and all further setup 93 | needs to be done manually. Check the `module.nix` if you're unsure of how to use the `yeetmouse` package 94 | manually. 95 | 96 | ```nix 97 | { pkgs, config, ... }: 98 | 99 | let 100 | yeetmouse = pkgs.yeetmouse.override { inherit (config.boot.kernelPackages) kernel; }; 101 | in { 102 | # This installs the yeetmouse kernel module: 103 | boot.extraModulePackages = [ yeetmouse ]; 104 | # This installs the yeetmouse GUI CLI package: 105 | environment.systemPackages = [ yeetmouse ]; 106 | services.udev = { 107 | # This installs the yeetmouse udev rules: 108 | packages = [ yeetmouse ]; 109 | 110 | # You may define a custom udev rule to apply default settings to the yeetmouse driver: 111 | extraRules = let 112 | echo = "${pkgs.coreutils}/bin/echo"; 113 | yeetmouseConfig = pkgs.writeShellScriptBin "yeetmouseConfig" '' 114 | ${echo} "1.0" > /sys/module/yeetmouse/parameters/Acceleration 115 | ${echo} "1" > /sys/module/yeetmouse/parameters/update 116 | ''; 117 | in '' 118 | SUBSYSTEMS=="usb|input|hid", ATTRS{bInterfaceClass}=="03", ATTRS{bInterfaceSubClass}=="01", ATTRS{bInterfaceProtocol}=="02", ATTRS{bInterfaceNumber}=="00", RUN+="${yeetmouseConfig}/bin/yeetmouseConfig" 119 | ''; 120 | }; 121 | } 122 | ``` 123 | 124 | Since the `pkgs.yeetmouse` package contains a kernel module, it'll be built against a specific version 125 | of the Linux kernel. By default this will be `pkgs.linxPackages.kernel` - the default version in nixpkgs. 126 | To override this, `.override { inherit (config.boot.kernelPackages) kernel; }` is called in the example 127 | above to use the selected kernel for your NixOS system instead. 128 | 129 | ## Flake Builds 130 | 131 | This flake exposes a `packages.${system}.yeetmouse` output, which allows you to build 132 | and test the `yeetmouse` package locally: 133 | 134 | ```sh 135 | nix build .#yeetmouse --json --keep-failed 136 | ``` 137 | 138 | ## Configuration Options 139 | 140 | The NixOS module exposes all configuration options that the Yeetmouse GUI exposes and aims to represent 141 | them with the exact terminology that the GUI represents them in, and in the same structure they'd appear 142 | in the GUI. 143 | 144 | ### Sensitivity and Anisotropy 145 | 146 | Sensitivity may be specified as a single float value. 147 | 148 | ```nix 149 | { 150 | hardware.yeetmouse = { 151 | enable = true; 152 | sensitivity = 1.0; 153 | }; 154 | } 155 | ``` 156 | 157 | But it may also be separated into separate sensitivity multiplies for the `x` and `y` axis to separate 158 | the sensitivity multipliers into horizontal and vertical values ("Anisotropy"). 159 | 160 | ```nix 161 | { 162 | hardware.yeetmouse = { 163 | enable = true; 164 | sensitivity = { 165 | x = 1.0; 166 | y = 0.8; 167 | }; 168 | }; 169 | } 170 | ``` 171 | 172 | ### Global Options 173 | 174 | The remaining global options on `hardware.yeetmouse` control the 175 | - `inputCap` (the maximum input pointer speed that's passed to the acceleration function) 176 | - `outputCap` (the maximum output pointer speed the acceleration function may produce) 177 | - `offset` (a curve offset applied to the acceleration function's input) 178 | - `preScale` (an input multiplier to adjust for Mouse DPI changes) 179 | All of these options are set to no-op values by default. If they're not altered, they have no effect. 180 | 181 | ```nix 182 | { 183 | hardware.yeetmouse = { 184 | enable = true; 185 | inputCap = 100.0; 186 | outputCap = 300.0; 187 | offset = -5.0; 188 | preScale = 0.5; 189 | }; 190 | } 191 | ``` 192 | 193 | ### Mouse Rotation and Snapping Angles 194 | 195 | A rotation angle may be applied to the pointer movement input. 196 | This adjusts the input to account for movement while the mouse is held at an angle. 197 | 198 | ```nix 199 | { 200 | hardware.yeetmouse = { 201 | enable = true; 202 | rotation.angle = 5.0; # in degrees 203 | }; 204 | } 205 | ``` 206 | 207 | Optionally, a snapping angle may be defined, which is an axis at an angle that the 208 | mouse movement will "snap" to when under a threshold. 209 | 210 | ```nix 211 | { 212 | hardware.yeetmouse = { 213 | enable = true; 214 | rotation = { 215 | snappingAngle = 45.0; # in degrees 216 | snappingThreshold = 2.0; # in degrees 217 | }; 218 | }; 219 | } 220 | ``` 221 | 222 | ### Acceleration Modes 223 | 224 | The acceleration modes are defined as exclusive sub-options on `hardware.yeetmouse.mode`. 225 | 226 | The `mode` option accepts any of the following mode options `linear`, `power`, `classic`, `motivity`, `jump`, and `lut`. 227 | These options are mutually exclusive and only one must be specified at a time. 228 | 229 | #### Linear 230 | 231 | Simplest acceleration mode. Accelerates at a constant rate by multiplying acceleration. 232 | 233 | ```nix 234 | { 235 | hardware.yeetmouse = { 236 | enable = true; 237 | mode.linear = { 238 | acceleration = 1.2; 239 | }; 240 | }; 241 | } 242 | ``` 243 | 244 | See [RawAccel: Linear](https://github.com/RawAccelOfficial/rawaccel/blob/5b39bb6/doc/Guide.md#linear) 245 | 246 | #### Power 247 | 248 | Acceleration mode based on an exponent and multiplier as found in Source Engine games. 249 | 250 | ```nix 251 | { 252 | hardware.yeetmouse = { 253 | enable = true; 254 | mode.power = { 255 | acceleration = 1.2; 256 | exponent = 0.2; 257 | }; 258 | }; 259 | } 260 | ``` 261 | 262 | See [RawAccel: Power](https://github.com/RawAccelOfficial/rawaccel/blob/5b39bb6/doc/Guide.md#power) 263 | 264 | #### Classic 265 | 266 | Acceleration mode based on an exponent and multiplier as found in Quake 3. 267 | 268 | ```nix 269 | { 270 | hardware.yeetmouse = { 271 | enable = true; 272 | mode.classic = { 273 | acceleration = 1.2; 274 | exponent = 0.2; 275 | }; 276 | }; 277 | } 278 | ``` 279 | 280 | See [RawAccel: Classic](https://github.com/RawAccelOfficial/rawaccel/blob/5b39bb6/doc/Guide.md#power) 281 | 282 | #### Motivity 283 | 284 | Acceleration mode based on a sigmoid function with a set mid-point. 285 | 286 | ```nix 287 | { 288 | hardware.yeetmouse = { 289 | enable = true; 290 | mode.motivity = { 291 | acceleration = 1.2; 292 | start = 10.0; 293 | }; 294 | }; 295 | } 296 | ``` 297 | 298 | See [RawAccel: Motivity](https://github.com/RawAccelOfficial/rawaccel/blob/5b39bb6/doc/Guide.md#motivity) 299 | 300 | #### Jump 301 | 302 | Acceleration mode applying gain above a mid-point. 303 | 304 | ```nix 305 | { 306 | hardware.yeetmouse = { 307 | enable = true; 308 | mode.jump = { 309 | acceleration = 2.0; 310 | midpoint = 5.0; 311 | smoothness = 0.2; 312 | useSmoothing = true; 313 | }; 314 | }; 315 | } 316 | ``` 317 | 318 | The transition at the midpoint is smoothened by default, which may be disabled by setting `useSmoothing = false;`. 319 | A smoothness is also applied to the whole sigmoid function which is controlled by `smoothness`. 320 | 321 | See [RawAccel: Jump](https://github.com/RawAccelOfficial/rawaccel/blob/5b39bb6/doc/Guide.md#jump) 322 | 323 | #### Look-up Table 324 | 325 | Acceleration mode following a custom curve. The curve is specified using individual `[x, y]` points. 326 | 327 | ```nix 328 | { 329 | hardware.yeetmouse = { 330 | enable = true; 331 | mode.lut = { 332 | # A list of points specified as [x y] tuples 333 | # NOTE: This is just an example and not a valid LUT 334 | data = [ 335 | [1.1 1.2] 336 | [5.2 4.8] 337 | [10.0 10.0] 338 | [60.0 40.0] 339 | ]; 340 | }; 341 | }; 342 | } 343 | ``` 344 | 345 | See [RawAccel: Lookup Table](https://github.com/RawAccelOfficial/rawaccel/blob/5b39bb6/doc/Guide.md#look-up-table) 346 | -------------------------------------------------------------------------------- /nix/flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1756349143, 6 | "narHash": "sha256-65oRt2D57ST5Gsa2Q4WCtpgCmKHEWt4+CQ1chWQ3Fos=", 7 | "owner": "NixOS", 8 | "repo": "nixpkgs", 9 | "rev": "c3e5d9f86b3f5678d0d8c9f0b9f081ab1ccdbe05", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "NixOS", 14 | "ref": "nixpkgs-unstable", 15 | "repo": "nixpkgs", 16 | "type": "github" 17 | } 18 | }, 19 | "root": { 20 | "inputs": { 21 | "nixpkgs": "nixpkgs" 22 | } 23 | } 24 | }, 25 | "root": "root", 26 | "version": 7 27 | } 28 | -------------------------------------------------------------------------------- /nix/flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "A fork of a fork of the Linux mouse driver with acceleration. Now with GUI and some other improvements!"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; 6 | }; 7 | 8 | outputs = inputs @ { self, nixpkgs }: let 9 | inherit (inputs.nixpkgs) lib; 10 | packageInputs = { 11 | shortRev = if (self ? shortRev) then self.shortRev else self.dirtyRev; 12 | }; 13 | eachSystem = lib.genAttrs ["aarch64-linux" "x86_64-linux"]; 14 | overlay = final: prev: { 15 | yeetmouse = import ./package.nix packageInputs final; 16 | }; 17 | in { 18 | inherit inputs; 19 | nixosModules.default = import ./module.nix overlay; 20 | overlays.default = overlay; 21 | packages = eachSystem (system: { 22 | yeetmouse = (import ./package.nix packageInputs nixpkgs.legacyPackages.${system}); 23 | }); 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /nix/package.nix: -------------------------------------------------------------------------------- 1 | { shortRev ? "dev" }: 2 | pkgs @ { 3 | lib, 4 | bash, 5 | stdenv, 6 | coreutils, 7 | writeShellScript, 8 | makeDesktopItem, 9 | kernel ? pkgs.linuxPackages.kernel, 10 | kernelModuleMakeFlags ? pkgs.linuxPackages.kernelModuleMakeFlags, 11 | ... 12 | }: 13 | 14 | let 15 | mkPackage = overrides @ { 16 | kernel, 17 | ... 18 | }: (stdenv.mkDerivation rec { 19 | pname = "yeetmouse"; 20 | version = shortRev; 21 | src = lib.fileset.toSource { 22 | root = ./..; 23 | fileset = ./..; 24 | }; 25 | 26 | setSourceRoot = "export sourceRoot=$(pwd)/source"; 27 | nativeBuildInputs = with pkgs; kernel.moduleBuildDependencies ++ [ 28 | makeWrapper 29 | autoPatchelfHook 30 | copyDesktopItems 31 | ]; 32 | buildInputs = [ 33 | stdenv.cc.cc.lib 34 | pkgs.glfw3 35 | ]; 36 | 37 | makeFlags = kernelModuleMakeFlags ++ [ 38 | "KBUILD_OUTPUT=${kernel.dev}/lib/modules/${kernel.modDirVersion}/build" 39 | "-C" 40 | "${kernel.dev}/lib/modules/${kernel.modDirVersion}/build" 41 | "M=$(sourceRoot)/driver" 42 | ]; 43 | 44 | preBuild = '' 45 | cp $sourceRoot/driver/config.sample.h $sourceRoot/driver/config.h 46 | ''; 47 | 48 | LD_LIBRARY_PATH = "/run/opengl-driver/lib:${lib.makeLibraryPath buildInputs}"; 49 | 50 | postBuild = '' 51 | make "-j$NIX_BUILD_CORES" -C $sourceRoot/gui "M=$sourceRoot/gui" "LIBS=-lglfw -lGL" 52 | ''; 53 | 54 | postInstall = let 55 | PATH = [ pkgs.zenity ]; 56 | in /*sh*/'' 57 | install -Dm755 $sourceRoot/gui/YeetMouseGui $out/bin/yeetmouse 58 | wrapProgram $out/bin/yeetmouse \ 59 | --prefix PATH : ${lib.makeBinPath PATH} 60 | ''; 61 | 62 | buildFlags = [ "modules" ]; 63 | installFlags = [ "INSTALL_MOD_PATH=${placeholder "out"}" ]; 64 | installTargets = [ "modules_install" ]; 65 | 66 | desktopItems = [ 67 | (makeDesktopItem { 68 | name = pname; 69 | exec = let 70 | xhost = "${pkgs.xorg.xhost}/bin/xhost"; 71 | in writeShellScript "yeetmouse.sh" /*bash*/ '' 72 | if [ "$XDG_SESSION_TYPE" = "wayland" ]; then 73 | ${xhost} +SI:localuser:root 74 | pkexec env DISPLAY="$DISPLAY" XAUTHORITY="$XAUTHORITY" "${pname}" 75 | ${xhost} -SI:localuser:root 76 | else 77 | pkexec env DISPLAY="$DISPLAY" XAUTHORITY="$XAUTHORITY" "${pname}" 78 | fi 79 | ''; 80 | type = "Application"; 81 | desktopName = "Yeetmouse GUI"; 82 | comment = "Yeetmouse Configuration Tool"; 83 | categories = [ 84 | "Settings" 85 | "HardwareSettings" 86 | ]; 87 | }) 88 | ]; 89 | 90 | meta.mainProgram = "yeetmouse"; 91 | }).overrideAttrs (prev: overrides); 92 | 93 | makeOverridable = 94 | f: origArgs: 95 | let 96 | origRes = f origArgs; 97 | in 98 | origRes // { override = newArgs: f (origArgs // newArgs); }; 99 | in 100 | makeOverridable mkPackage { inherit kernel; } 101 | -------------------------------------------------------------------------------- /pkg/PKGBUILD.template: -------------------------------------------------------------------------------- 1 | # Maintainer: Klaus Zipfel 2 | # Contributor: Christopher Williams 3 | 4 | pkgbase=yeetmouse 5 | pkgname=('yeetmouse-driver-dkms') 6 | pkgver=__VERSION__ 7 | pkgrel=1 8 | pkgdesc="USB HID Boot Protocol mouse driver with acceleration." 9 | arch=('i686' 'x86_64') 10 | url="https://github.com/AndyFilter/YeetMouse" 11 | license=('GPL2') 12 | #makedepends=('python-setuptools') 13 | source=("__SRC__") 14 | sha256sums=('__HASH__') 15 | 16 | # Currently we only supply the driver 17 | package_yeetmouse-driver-dkms() { 18 | pkgdesc="Kernel driver for YEETMOUSE (DKMS-variant)" 19 | depends=('dkms') 20 | install=yeetmouse-driver-dkms.install 21 | 22 | cd "$pkgbase-$pkgver" 23 | echo $pkgbase-$pkgver 24 | make DESTDIR="$pkgdir" setup_dkms 25 | } 26 | -------------------------------------------------------------------------------- /pkg/yeetmouse-driver-dkms.install: -------------------------------------------------------------------------------- 1 | pre_remove() { 2 | # Unbind all currently bound mice from yeetmouse and rebind them to usbhid 3 | /usr/lib/udev/yeetmouse_manage unbind_all 4 | } 5 | 6 | post_remove() { 7 | # Unload the kernel module from memory 8 | rmmod yeetmouse 9 | } 10 | -------------------------------------------------------------------------------- /scripts/bind.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # TODO This proof-of-concept bind script will be replaced by a more mainstreamed udev rule in the near future. 4 | 5 | # Home PC 6 | DEVICES=( "5-1.1:1.1" "1-3:1.2" ) 7 | # Office PC 8 | #DEVICES=( "2-1.3.4:1.1" ) 9 | # Lab PC 10 | #DEVICES=( "3-2.5.4:1.1" "3-1.1.3:1.1" ) 11 | 12 | # How to get the device id 13 | # Identify your mouse via 'lsusb' 14 | # Bus 003 Device 004: ID 1038:1729 SteelSeries ApS 15 | # So your mouse is on Bus 3 and has the id 4 16 | 17 | # Now dive in deeper with 'lsusb -t' and check out "Bus 3" in detail 18 | #/: Bus 03.Port 1: Dev 1, Class=root_hub, Driver=ehci-pci/2p, 480M 19 | # |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/8p, 480M 20 | # |__ Port 1: Dev 3, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M 21 | # |__ Port 1: Dev 3, If 1, Class=Human Interface Device, Driver=usbhid, 1.5M 22 | # |__ Port 2: Dev 4, If 0, Class=Human Interface Device, Driver=usbhid, 12M 23 | # |__ Port 2: Dev 4, If 1, Class=Human Interface Device, Driver=usbhid, 12M 24 | 25 | # The node with 'Dev 4' tells you, your mouse is on Bus 3, Port 1, Port 2 26 | 27 | # Now do a sanity check with 'ls /sys/bus/usb/devices' 28 | #1-0:1.0 1-1.3 1-1.5 1-1.5:1.1 2-0:1.0 3-1 3-1.1:1.0 3-1.2 3-1.2:1.1 4-0:1.0 6-0:1.0 usb2 usb4 usb6 29 | #1-1 1-1.3:1.0 1-1.5:1.0 1-1:1.0 3-0:1.0 3-1.1 3-1.1:1.1 3-1.2:1.0 3-1:1.0 5-0:1.0 usb1 usb3 usb5 30 | 31 | # -> Select the matching sub-device. This really much depends on your mouse. It can be the "1.0". For my SteelSeries mice, it was always "1.1". So here it is 3-1.2:1.1 (Bus3-Port1.Port2:SubDevice) 32 | 33 | for DEVICE in "${DEVICES[@]}"; do 34 | echo "Rebinding $DEVICE" 35 | echo -n "$DEVICE" > /sys/bus/usb/drivers/usbhid/unbind 36 | echo -n "$DEVICE" > /sys/bus/usb/drivers/yeetmouse/bind 37 | done 38 | -------------------------------------------------------------------------------- /scripts/build_arch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ROOT="$(git rev-parse --show-toplevel)" 4 | 5 | # Read current version 6 | VERSION=$(cat "${ROOT}/Makefile" | grep -oP "(?<=DKMS_VER\?=)[0-9\.]+") 7 | SRC_NAME=yeetmouse-$VERSION 8 | 9 | SRC=$SRC_NAME.tar.xz 10 | 11 | # Create PKGBUILD for AUR and the bin package below for release via github 12 | if [ "$1" = "aur" ]; then 13 | SRC='https://github.com/AndyFilter/YeetMouse/releases/download/v$pkgver/yeetmouse-$pkgver.tar.xz' 14 | echo $SRC 15 | fi 16 | 17 | # Clear the build folder from old releases 18 | rm -rf $ROOT/pkg/build/ 19 | 20 | # Create new package file 21 | . $ROOT/scripts/build_pkg.sh 22 | 23 | # ########## Generate PKGBUILD for Arch based systems 24 | HASH=$(sha256sum "${ROOT}/pkg/build/$SRC_NAME.tar.xz" | awk '{ print $1 }') 25 | cp -f "${ROOT}/pkg/PKGBUILD.template" "${ROOT}/pkg/build/PKGBUILD" 26 | sed -i 's|'__VERSION__'|'$VERSION'|' "${ROOT}/pkg/build/PKGBUILD" 27 | sed -i 's|'__HASH__'|'$HASH'|' "${ROOT}/pkg/build/PKGBUILD" 28 | sed -i 's|'__SRC__'|'$SRC'|' "${ROOT}/pkg/build/PKGBUILD" 29 | 30 | # Copy "install" file to build 31 | cp -f "${ROOT}/pkg/yeetmouse-driver-dkms.install" "${ROOT}/pkg/build/" 32 | 33 | # When we build the AUR package, we do not want to do the next steps 34 | if [ "$1" = "aur" ]; then 35 | exit 36 | fi 37 | 38 | cd $ROOT/pkg/build/ 39 | makepkg -f 40 | -------------------------------------------------------------------------------- /scripts/build_pkg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | TEMP_DIR=$(mktemp --suffix="_pkg_build_tmp" -d) 4 | ROOT="$(git rev-parse --show-toplevel)" 5 | 6 | # Read current version 7 | VERSION=$(cat "${ROOT}/Makefile" | grep -oP "(?<=DKMS_VER\?=)[0-9\.]+") 8 | SRC_NAME=yeetmouse-$VERSION 9 | 10 | # ########## Create output director 11 | mkdir -p "${ROOT}/pkg/build" 12 | 13 | # ########## Creates a compressed file for releases on github, mkpkg etc 14 | # Tar, copy, untar, tar.xz: I know, this is stupid, but atleast I can skip all git ignored files with that method and control the base-folder in the tar.xz file 15 | tar --exclude-vcs --exclude-vcs-ignores -cf $TEMP_DIR/tmp.tar -C "${ROOT}" . 16 | 17 | cd $TEMP_DIR 18 | rm -rf $SRC_NAME > /dev/null 19 | mkdir -p $SRC_NAME 20 | tar -xf tmp.tar -C $SRC_NAME 21 | 22 | # HACK HACK HACK We purposedly ignored config.h earlier with the "--exclude-vcs --exclude-vcs-ignores" flags. Since config.h (if it exists) is in .gitignore, we need to add it manually. 23 | cp ${ROOT}/driver/config.h $SRC_NAME/driver/ 24 | 25 | tar -cJf $SRC_NAME.tar.xz $SRC_NAME 26 | 27 | # ########## Move compressed file to build folder 28 | cd ${ROOT}/pkg/ 29 | mv $TEMP_DIR/$SRC_NAME.tar.xz "${ROOT}/pkg/build/" 30 | -------------------------------------------------------------------------------- /shared_definitions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef SHARED_DEFINITIONS_H 4 | #define SHARED_DEFINITIONS_H 5 | 6 | enum AccelMode { 7 | AccelMode_Current = 0, // Mainly used in GUI, denotes lack of a curve on the driver side 8 | AccelMode_Linear = 1, 9 | AccelMode_Power = 2, 10 | AccelMode_Classic = 3, 11 | AccelMode_Motivity = 4, 12 | AccelMode_Synchronous = 5, 13 | AccelMode_Natural = 6, 14 | AccelMode_Jump = 7, 15 | AccelMode_Lut = 8, 16 | AccelMode_CustomCurve = 9, 17 | AccelMode_Count, 18 | }; 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.28) 2 | project(YeetMouseTests) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | # Add flags based on build type 7 | if (CMAKE_BUILD_TYPE STREQUAL "Debug") 8 | message(STATUS "Configuring with debug flags.") 9 | message(STATUS "Debug build selected. Compiling with debug information and no optimization.") 10 | add_compile_options(-g -O0) 11 | add_link_options(-g) 12 | elseif (CMAKE_BUILD_TYPE STREQUAL "Release") 13 | message(STATUS "Configuring with release flags.") 14 | message(STATUS "Release build selected. Compiling with full optimization and assertions disabled.") 15 | add_compile_options(-O3 -DNDEBUG) 16 | else() 17 | message(STATUS "Configuring with custom build flags.") 18 | message(STATUS "Custom build type selected. Compiling with debug information and moderate optimization.") 19 | add_compile_options(-g -O2) 20 | add_link_options(-g) 21 | endif() 22 | 23 | if (CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc64le") 24 | message(STATUS "Configuring for ppc64le architecture. Adding __ppc64le__ definition.") 25 | add_compile_definitions(__ppc64le__) 26 | endif() 27 | 28 | file(GLOB driver_source "../driver/accel_modes.[c|h]") 29 | file(GLOB fixedpoint_source "../driver/FixedMath/*") 30 | 31 | # Copy driver source files over 32 | file(COPY ${driver_source} DESTINATION "${CMAKE_HOME_DIRECTORY}/driver") 33 | file(COPY ${fixedpoint_source} DESTINATION "${CMAKE_HOME_DIRECTORY}/driver/FixedMath") 34 | file(COPY "../shared_definitions.h" DESTINATION "${CMAKE_HOME_DIRECTORY}") 35 | 36 | # Run a python script to clean-up the freshly copied source files 37 | execute_process(COMMAND python3 CleanUpFiles.py 38 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) 39 | 40 | add_executable(YeetMouseTests main.cpp driver/accel_modes.c 41 | driver/config.h 42 | TestManager.cpp 43 | TestManager.h 44 | Tests.cpp 45 | Tests.h 46 | ../gui/FunctionHelper.cpp) 47 | -------------------------------------------------------------------------------- /tests/CleanUpFiles.py: -------------------------------------------------------------------------------- 1 | import os 2 | from time import sleep 3 | 4 | sleep(0.2) 5 | 6 | header_file = open("driver/accel_modes.h", "r") 7 | header_contents = header_file.read() 8 | header_file.close() 9 | header_contents = """#ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | """ + header_contents + """ 14 | 15 | #ifdef __cplusplus 16 | } 17 | #endif""" 18 | 19 | sleep(0.1) # https://preview.redd.it/just-add-sleep-v0-iak44ctwyfba1.jpg?auto=webp&s=352bb4ea528518a97a36600b1271535cf9852b4a 20 | 21 | open("driver/accel_modes.h", "w").write(header_contents) 22 | 23 | # fixed_point_file = open("driver/FixedMath/Fixed64.h", "r") 24 | # fixed_point_contents = fixed_point_file.read() 25 | # fixed_point_file.close() 26 | # 27 | # fixed_point_contents.replace("// static float FP64_ToFloat(FP_LONG v) {", "") 28 | # 29 | # open("driver/FixedMath/Fixed64.h", "w").write(fixed_point_contents) -------------------------------------------------------------------------------- /tests/Readme.md: -------------------------------------------------------------------------------- 1 | # Testing Suite 2 | 3 | This is an easy to run and expand testing suite meant for unit testing (code testing). 4 | It simply copies the code running on the driver side and runs tests on it. 5 | 6 | To run, first build CMake to copy all the necessary files from the driver and modify them with the python script. 7 | 8 | Add new testcases in the `Tests.cpp` file. 9 | New testcases *should* follow this template: 10 | ```c++ 11 | TestSupervisor supervisor{"Linear Mode"}; // The supervisor handles switching between the tests and gathers the results 12 | 13 | try { // The tests should be inside try-catch block 14 | supervisor.NextTest(); // Every test starts with this, it does all the prining 15 | 16 | TestManager::SetAccelMode({ACCEL_MODE}); 17 | // Set all the parameters that the function uses beforehand 18 | TestManager::SetAcceleration(0.0001f); // Acceleration set to 0.0001 for example 19 | TestManager::UpdateModesConstants(); 20 | 21 | for (int i = 0; i < BASIC_TEST_STEPS; i++) { // Loop over some range of input values 22 | float value = range_min + static_cast(i) * (range_max - range_min) / BASIC_TEST_STEPS; // map values 23 | auto res = TestManager::Accel{ACCEL_MODE}(value); 24 | 25 | supervisor.Validate(IsAccelValueGood(res)); 26 | //supervisor.Validate(IsCloseEnough(res, TestManager::EvalFloatFunc(value))); 27 | supervisor.Validate(IsCloseEnoughRelative(res, TestManager::EvalFloatFunc(value))); 28 | } 29 | 30 | ... // Other test cases 31 | } 32 | catch (std::exception &ex) { 33 | fprintf(stderr, "Exception: %s, in {ACCEL_MODE} mode\n", ex.what()); 34 | supervisor.result = false; 35 | } 36 | ``` 37 | Where `{ACCEL_MODE}` is the mode the testcase is written for (e.g. `Linear`). 38 | 39 | If You want to test an accel mode for a bunch of different parameters in a loop, it's easier to call it by passing all the parameters it uses, like so: 40 | ```c++ 41 | auto res = TestManager::AccelPower(x, accel, exp, mid); 42 | ``` 43 | This calls the `Power` function with all three parameters (internally it does the same as the first example, which is setting the parameters one by one and at the end calling `UpdateModesConstants()`). 44 | 45 | If, on the other hand, You want to test the validation part on the driver's code (`update_constants` function), You can instead of the `for` loop use this: 46 | ```c++ 47 | if (!TestManager::ValidateConstants()) { 48 | fprintf(stderr, "Invalid constants\n"); 49 | temp_res = false; 50 | } 51 | ``` 52 | This checks if the constants after the update are valid (internally checks if the accel mode is set to `AccelMode_Current`, which on the driver side means there was an error). -------------------------------------------------------------------------------- /tests/TestManager.cpp: -------------------------------------------------------------------------------- 1 | #include "TestManager.h" 2 | 3 | #include "shared_definitions.h" 4 | #include "driver/accel_modes.h" 5 | 6 | // "Private" values only visible to the accel_modes 7 | FP_LONG g_Acceleration = 0, g_Exponent = 0, g_Midpoint = 0, g_Motivity = 0, g_RotationAngle = 0, g_AngleSnap_Angle = 0, 8 | g_AngleSnap_Threshold = 0, g_LutData_x[256], g_LutData_y[256]; 9 | char g_AccelerationMode = 0, g_UseSmoothing = 0; 10 | unsigned long g_LutSize = 0; 11 | ModesConstants modesConst; 12 | static CachedFunction function; 13 | 14 | // TestManager & TestManager::GetInstance() { 15 | // static TestManager instance; 16 | // return instance; 17 | // } 18 | 19 | void TestManager::Initialize() { 20 | function.params = new Parameters; 21 | function.params->sens = 1; 22 | function.params->sensY = 1; 23 | function.params->accelMode = static_cast(g_AccelerationMode); 24 | function.params->preScale = 1; 25 | function.params->accel = FP64_ToFloat(g_Acceleration); 26 | function.params->exponent = FP64_ToFloat(g_Exponent); 27 | function.params->midpoint = FP64_ToFloat(g_Midpoint); 28 | function.params->offset = 0; 29 | function.params->useSmoothing = g_UseSmoothing; 30 | function.params->rotation = FP64_ToFloat(g_RotationAngle); 31 | function.params->as_angle = FP64_ToFloat(g_AngleSnap_Angle); 32 | function.params->as_threshold = FP64_ToFloat(g_AngleSnap_Threshold); 33 | function.params->inCap = 0; 34 | function.params->outCap = 0; 35 | function.PreCacheConstants(); 36 | } 37 | 38 | FP_LONG TestManager::AccelLinear(FP_LONG x, FP_LONG acceleration, FP_LONG midpoint, bool gain) { 39 | SetAcceleration(acceleration); 40 | SetUseSmoothing(gain); 41 | SetMidpoint(midpoint); 42 | UpdateModesConstants(); 43 | return accel_linear(x); 44 | } 45 | 46 | FP_LONG TestManager::AccelPower(FP_LONG x, FP_LONG acceleration, FP_LONG exponent, FP_LONG midpoint, FP_LONG motivity, 47 | bool gain) { 48 | SetAcceleration(acceleration); 49 | SetExponent(exponent); 50 | SetMidpoint(midpoint); 51 | SetMotivity(motivity); 52 | SetUseSmoothing(gain); 53 | UpdateModesConstants(); 54 | return accel_power(x); 55 | } 56 | 57 | FP_LONG TestManager::AccelClassic(FP_LONG x, FP_LONG acceleration, FP_LONG exponent, FP_LONG midpoint, bool gain) { 58 | SetAcceleration(acceleration); 59 | SetExponent(exponent); 60 | SetMidpoint(midpoint); 61 | SetUseSmoothing(gain); 62 | UpdateModesConstants(); 63 | return accel_classic(x); 64 | } 65 | 66 | FP_LONG TestManager::AccelMotivity(FP_LONG x, FP_LONG acceleration, FP_LONG exponent, FP_LONG midpoint) { 67 | SetAcceleration(acceleration); 68 | SetExponent(exponent); 69 | SetMidpoint(midpoint); 70 | UpdateModesConstants(); 71 | return accel_motivity(x); 72 | } 73 | 74 | FP_LONG TestManager::AccelSynchronous(FP_LONG x, FP_LONG sync_speed, FP_LONG gamma, FP_LONG smoothness, 75 | FP_LONG motivity, bool gain) { 76 | SetAcceleration(sync_speed); 77 | SetExponent(gamma); 78 | SetMidpoint(smoothness); 79 | SetMotivity(motivity); 80 | SetUseSmoothing(gain); 81 | UpdateModesConstants(); 82 | return accel_motivity(x); 83 | } 84 | 85 | FP_LONG TestManager::AccelJump(FP_LONG x, FP_LONG acceleration, FP_LONG exponent, FP_LONG midpoint, bool gain) { 86 | SetAcceleration(acceleration); 87 | SetExponent(exponent); 88 | SetMidpoint(midpoint); 89 | SetUseSmoothing(gain); 90 | UpdateModesConstants(); 91 | return accel_jump(x); 92 | } 93 | 94 | FP_LONG TestManager::AccelLUT(FP_LONG x, FP_LONG values_x[], FP_LONG values_y[], unsigned long count) { 95 | SetLutSize(count); 96 | SetLutData_x(values_x, count); 97 | SetLutData_y(values_y, count); 98 | UpdateModesConstants(); 99 | return accel_lut(x); 100 | } 101 | 102 | FP_LONG TestManager::AccelLUT(FP_LONG x) { 103 | return accel_lut(x); 104 | } 105 | 106 | FP_LONG TestManager::AccelLinear(float x, float acceleration, float midpoint, bool gain) { 107 | return AccelLinear(FP64_FromFloat(x), FP64_FromFloat(acceleration), FP64_FromFloat(midpoint), gain); 108 | } 109 | 110 | FP_LONG TestManager::AccelPower(float x, float acceleration, float exponent, float midpoint, float motivity, 111 | bool gain) { 112 | return AccelPower(FP64_FromFloat(x), FP64_FromFloat(acceleration), FP64_FromFloat(exponent), 113 | FP64_FromFloat(midpoint), FP64_FromFloat(motivity), gain); 114 | } 115 | 116 | FP_LONG TestManager::AccelClassic(float x, float acceleration, float exponent, float midpoint, bool gain) { 117 | return AccelClassic(FP64_FromFloat(x), FP64_FromFloat(acceleration), FP64_FromFloat(exponent), 118 | FP64_FromFloat(midpoint), gain); 119 | } 120 | 121 | FP_LONG TestManager::AccelMotivity(float x, float acceleration, float exponent, float midpoint) { 122 | return AccelMotivity(FP64_FromFloat(x), FP64_FromFloat(acceleration), FP64_FromFloat(exponent), 123 | FP64_FromFloat(midpoint)); 124 | } 125 | 126 | FP_LONG TestManager::AccelSynchronous(float x, float sync_speed, float gamma, float smoothness, float motivity, 127 | bool gain) { 128 | return AccelSynchronous(FP64_FromFloat(x), FP64_FromFloat(sync_speed), FP64_FromFloat(gamma), 129 | FP64_FromFloat(smoothness), FP64_FromFloat(motivity), gain); 130 | } 131 | 132 | FP_LONG TestManager::AccelJump(float x, float acceleration, float exponent, float midpoint, bool gain) { 133 | return AccelJump(FP64_FromFloat(x), FP64_FromFloat(acceleration), FP64_FromFloat(exponent), 134 | FP64_FromFloat(midpoint), gain); 135 | } 136 | 137 | FP_LONG TestManager::AccelLUT(float x, float values_x[], float values_y[], unsigned long count) { 138 | auto *values_x_fp = new FP_LONG[count]; 139 | auto *values_y_fp = new FP_LONG[count]; 140 | for (unsigned long i = 0; i < count; i++) { 141 | values_x_fp[i] = FP64_FromFloat(values_x[i]); 142 | values_y_fp[i] = FP64_FromFloat(values_y[i]); 143 | } 144 | FP_LONG result = AccelLUT(FP64_FromFloat(x), values_x_fp, values_y_fp, count); 145 | delete[] values_x_fp; 146 | delete[] values_y_fp; 147 | return result; 148 | } 149 | 150 | FP_LONG TestManager::AccelLUT(float x) { 151 | return AccelLUT(FP64_FromFloat(x)); 152 | } 153 | 154 | FP_LONG TestManager::AccelLinear(float x) { 155 | return accel_linear(FP64_FromFloat(x)); 156 | } 157 | 158 | FP_LONG TestManager::AccelPower(float x) { 159 | return accel_power(FP64_FromFloat(x)); 160 | } 161 | 162 | FP_LONG TestManager::AccelClassic(float x) { 163 | return accel_classic(FP64_FromFloat(x)); 164 | } 165 | 166 | FP_LONG TestManager::AccelMotivity(float x) { 167 | return accel_motivity(FP64_FromFloat(x)); 168 | } 169 | 170 | FP_LONG TestManager::AccelSynchronous(float x) { 171 | return accel_synchronous(FP64_FromFloat(x)); 172 | } 173 | 174 | FP_LONG TestManager::AccelNatural(float x) { 175 | return accel_natural(FP64_FromFloat(x)); 176 | } 177 | 178 | FP_LONG TestManager::AccelJump(float x) { 179 | return accel_jump(FP64_FromFloat(x)); 180 | } 181 | 182 | ModesConstants &TestManager::GetModesConstants() { 183 | return modesConst; 184 | } 185 | 186 | void TestManager::UpdateModesConstants() { 187 | update_constants(); 188 | function.PreCacheConstants(); 189 | } 190 | 191 | bool TestManager::ValidateConstants() { 192 | if (g_AccelerationMode == AccelMode_Current) 193 | return false; 194 | 195 | // switch (g_AccelerationMode) { 196 | // case AccelMode_Linear: 197 | // break; 198 | // case AccelMode_Power: 199 | // break; 200 | // case AccelMode_Classic: 201 | // break; 202 | // case AccelMode_Motivity: 203 | // break; 204 | // case AccelMode_Jump: 205 | // break; 206 | // case AccelMode_Lut: case AccelMode_CustomCurve: 207 | // break; 208 | // } 209 | 210 | return true; 211 | } 212 | 213 | bool TestManager::ValidateFunctionGUI() { 214 | return function.ValidateSettings(); 215 | } 216 | 217 | void TestManager::SetAccelMode(AccelMode mode) { 218 | g_AccelerationMode = mode; 219 | function.params->accelMode = static_cast(g_AccelerationMode); 220 | } 221 | 222 | void TestManager::SetUseSmoothing(char useSmoothing) { 223 | g_UseSmoothing = useSmoothing; 224 | function.params->useSmoothing = g_UseSmoothing; 225 | } 226 | 227 | void TestManager::SetAcceleration(FP_LONG acceleration) { 228 | g_Acceleration = acceleration; 229 | function.params->accel = FP64_ToFloat(g_Acceleration); 230 | } 231 | 232 | void TestManager::SetExponent(FP_LONG exponent) { 233 | g_Exponent = exponent; 234 | function.params->exponent = FP64_ToFloat(g_Exponent); 235 | } 236 | 237 | void TestManager::SetMidpoint(FP_LONG midpoint) { 238 | g_Midpoint = midpoint; 239 | function.params->midpoint = FP64_ToFloat(g_Midpoint); 240 | } 241 | 242 | void TestManager::SetMotivity(FP_LONG motivity) { 243 | g_Motivity = motivity; 244 | function.params->motivity = FP64_ToFloat(g_Motivity); 245 | } 246 | 247 | void TestManager::SetRotationAngle(FP_LONG rotationAngle) { 248 | g_RotationAngle = rotationAngle; 249 | function.params->rotation = FP64_ToFloat(g_RotationAngle); 250 | } 251 | 252 | void TestManager::SetAngleSnap_Angle(FP_LONG angleSnap_Angle) { 253 | g_AngleSnap_Angle = angleSnap_Angle; 254 | function.params->as_angle = FP64_ToFloat(g_AngleSnap_Angle); 255 | } 256 | 257 | void TestManager::SetAngleSnap_Threshold(FP_LONG angleSnap_Threshold) { 258 | g_AngleSnap_Threshold = angleSnap_Threshold; 259 | function.params->as_threshold = FP64_ToFloat(g_AngleSnap_Threshold); 260 | } 261 | 262 | void TestManager::SetUseSmoothing(bool useSmoothing) { 263 | g_UseSmoothing = useSmoothing ? 1 : 0; 264 | function.params->useSmoothing = g_UseSmoothing; 265 | } 266 | 267 | void TestManager::SetLutSize(unsigned long lutSize) { 268 | g_LutSize = lutSize; 269 | function.params->LUT_size = g_LutSize; 270 | } 271 | 272 | void TestManager::SetLutData_x(FP_LONG values[], unsigned long count) { 273 | SetLutSize(count); 274 | 275 | for (unsigned long i = 0; i < count; i++) { 276 | g_LutData_x[i] = values[i]; 277 | function.params->LUT_data_x[i] = FP64_ToFloat(values[i]); 278 | } 279 | } 280 | 281 | void TestManager::SetLutData_y(FP_LONG values[], unsigned long count) { 282 | SetLutSize(count); 283 | 284 | for (unsigned long i = 0; i < count; i++) { 285 | g_LutData_y[i] = values[i]; 286 | function.params->LUT_data_y[i] = FP64_ToFloat(values[i]); 287 | } 288 | } 289 | 290 | void TestManager::SetLutData(FP_LONG values_x[], FP_LONG values_y[], unsigned long count) { 291 | SetLutSize(count); 292 | SetLutData_x(values_x, count); 293 | SetLutData_y(values_y, count); 294 | } 295 | 296 | void TestManager::SetAcceleration(float acceleration) { 297 | SetAcceleration(FP64_FromFloat(acceleration)); 298 | } 299 | 300 | void TestManager::SetExponent(float exponent) { 301 | SetExponent(FP64_FromFloat(exponent)); 302 | } 303 | 304 | void TestManager::SetMidpoint(float midpoint) { 305 | SetMidpoint(FP64_FromFloat(midpoint)); 306 | } 307 | 308 | void TestManager::SetMotivity(float motivity) { 309 | SetMotivity(FP64_FromFloat(motivity)); 310 | } 311 | 312 | void TestManager::SetRotationAngle(float rotationAngle) { 313 | SetRotationAngle(FP64_FromFloat(rotationAngle)); 314 | } 315 | 316 | void TestManager::SetAngleSnap_Angle(float angleSnap_Angle) { 317 | SetAngleSnap_Angle(FP64_FromFloat(angleSnap_Angle)); 318 | } 319 | 320 | void TestManager::SetAngleSnap_Threshold(float angleSnap_Threshold) { 321 | SetAngleSnap_Threshold(FP64_FromFloat(angleSnap_Threshold)); 322 | } 323 | 324 | void TestManager::SetLutData(float values_x[], float values_y[], unsigned long count) { 325 | auto *values_x_fp = new FP_LONG[count]; 326 | auto *values_y_fp = new FP_LONG[count]; 327 | for (unsigned long i = 0; i < count; i++) { 328 | values_x_fp[i] = FP64_FromFloat(values_x[i]); 329 | values_y_fp[i] = FP64_FromFloat(values_y[i]); 330 | } 331 | SetLutData(values_x_fp, values_y_fp, count); 332 | delete[] values_x_fp; 333 | delete[] values_y_fp; 334 | } 335 | 336 | float TestManager::EvalFloatFunc(float x) { 337 | function.params->accelMode = static_cast(g_AccelerationMode); 338 | return function.EvalFuncAt(x); 339 | } 340 | -------------------------------------------------------------------------------- /tests/TestManager.h: -------------------------------------------------------------------------------- 1 | #ifndef TESTMANAGER_H 2 | #define TESTMANAGER_H 3 | 4 | #include "shared_definitions.h" 5 | #include "driver/accel_modes.h" 6 | #include "../gui/FunctionHelper.h" 7 | 8 | /// 9 | /// Class for interfacing with the 'fake' driver 10 | /// 11 | class TestManager { 12 | public: 13 | //static TestManager& GetInstance(); 14 | static void Initialize(); 15 | 16 | static FP_LONG AccelLinear(FP_LONG x, FP_LONG acceleration, FP_LONG midpoint, bool gain); 17 | static FP_LONG AccelPower(FP_LONG x, FP_LONG acceleration, FP_LONG exponent, FP_LONG midpoint, FP_LONG motivity, 18 | bool gain); 19 | static FP_LONG AccelClassic(FP_LONG x, FP_LONG acceleration, FP_LONG exponent, FP_LONG midpoint, bool gain); 20 | static FP_LONG AccelMotivity(FP_LONG x, FP_LONG acceleration, FP_LONG exponent, FP_LONG midpoint); 21 | static FP_LONG AccelSynchronous(FP_LONG x, FP_LONG sync_speed, FP_LONG gamma, FP_LONG smoothness, FP_LONG motivity, 22 | bool gain); 23 | static FP_LONG AccelJump(FP_LONG x, FP_LONG acceleration, FP_LONG exponent, FP_LONG midpoint, bool gain); 24 | static FP_LONG AccelLUT(FP_LONG x, FP_LONG values_x[], FP_LONG values_y[], unsigned long count); 25 | static FP_LONG AccelLUT(FP_LONG x); 26 | 27 | static FP_LONG AccelLinear(float x, float acceleration, float midpoint, bool gain); 28 | static FP_LONG AccelPower(float x, float acceleration, float exponent, float midpoint, float motivity, bool gain); 29 | static FP_LONG AccelClassic(float x, float acceleration, float exponent, float midpoint, bool gain); 30 | static FP_LONG AccelMotivity(float x, float acceleration, float exponent, float midpoint); 31 | static FP_LONG AccelSynchronous(float x, float sync_speed, float gamma, float smoothness, float motivity, 32 | bool gain); 33 | static FP_LONG AccelJump(float x, float acceleration, float exponent, float midpoint, bool gain); 34 | static FP_LONG AccelLUT(float x, float values_x[], float values_y[], unsigned long count); 35 | 36 | static FP_LONG AccelLinear(float x); // Parameter values set manually! 37 | static FP_LONG AccelPower(float x); // Parameter values set manually! 38 | static FP_LONG AccelClassic(float x); // Parameter values set manually! 39 | static FP_LONG AccelMotivity(float x); // Parameter values set manually! 40 | static FP_LONG AccelSynchronous(float x); // Parameter values set manually! 41 | static FP_LONG AccelNatural(float x); // Parameter values set manually! 42 | static FP_LONG AccelJump(float x); // Parameter values set manually! 43 | static FP_LONG AccelLUT(float x); // Parameter values set manually! 44 | 45 | static ModesConstants &GetModesConstants(); 46 | static void UpdateModesConstants(); 47 | static bool ValidateConstants(); 48 | static bool ValidateFunctionGUI(); 49 | 50 | static void SetAccelMode(AccelMode mode); 51 | static void SetUseSmoothing(char useSmoothing); 52 | static void SetAcceleration(FP_LONG acceleration); 53 | static void SetExponent(FP_LONG exponent); 54 | static void SetMidpoint(FP_LONG midpoint); 55 | static void SetMotivity(FP_LONG motivity); 56 | static void SetRotationAngle(FP_LONG rotationAngle); 57 | static void SetAngleSnap_Angle(FP_LONG angleSnap_Angle); 58 | static void SetAngleSnap_Threshold(FP_LONG angleSnap_Threshold); 59 | static void SetUseSmoothing(bool useSmoothing); 60 | static void SetLutSize(unsigned long lutSize); 61 | static void SetLutData_x(FP_LONG values[], unsigned long count); 62 | static void SetLutData_y(FP_LONG values[], unsigned long count); 63 | static void SetLutData(FP_LONG values_x[], FP_LONG values_y[], unsigned long count); 64 | 65 | static void SetAcceleration(float acceleration); 66 | static void SetExponent(float exponent); 67 | static void SetMidpoint(float midpoint); 68 | static void SetMotivity(float motivity); 69 | static void SetRotationAngle(float rotationAngle); 70 | static void SetAngleSnap_Angle(float angleSnap_Angle); 71 | static void SetAngleSnap_Threshold(float angleSnap_Threshold); 72 | static void SetLutData(float values_x[], float values_y[], unsigned long count); 73 | 74 | // static float EvalFloatLinear(float x); 75 | // static float EvalFloatPower(float x); 76 | // static float EvalFloatClassic(float x); 77 | // static float EvalFloatMotivity(float x); 78 | // static float EvalFloatJump(float x); 79 | // static float EvalFloatLUT(float x); 80 | 81 | static float EvalFloatFunc(float x); 82 | }; 83 | 84 | 85 | #endif //TESTMANAGER_H 86 | -------------------------------------------------------------------------------- /tests/Tests.h: -------------------------------------------------------------------------------- 1 | #ifndef TESTS_H 2 | #define TESTS_H 3 | 4 | 5 | #include 6 | #include "../shared_definitions.h" 7 | #include "driver/config.h" 8 | #include "driver/FixedMath/Fixed64.h" 9 | #include 10 | 11 | #define BASIC_TEST_STEPS 1000 12 | #define BASIC_TEST_STEPS_REDUCED 100 13 | #define BASIC_TEST_RANGE_MAX 150 14 | 15 | #define RESET "\033[0m" 16 | #define RED "\033[31m" // Red 17 | #define GREEN "\033[32m" // Green 18 | 19 | /// 20 | /// Class for creating and running highly customizable tests 21 | /// 22 | class Tests { 23 | public: 24 | static void Initialize(); 25 | 26 | /// Test functions 27 | /// @param range_min minimum speed testing range 28 | /// @param range_max maximum speed testing range 29 | /// @return True - passed, False - Failed 30 | /// 31 | /// @note Use 'TestManager::Set{Parameter}()' to set parameters (once done, call TestManager::UpdateModesConstants()) 32 | /// before calling 'TestManager::Accel{AccelMode}()', 33 | /// or call 'TestManager::Accel{AccelMode}({parameters})' and pass all the parameters 34 | static bool TestAccelLinear(float range_min = 0, float range_max = BASIC_TEST_RANGE_MAX); 35 | static bool TestAccelPower(float range_min = 0, float range_max = BASIC_TEST_RANGE_MAX); 36 | static bool TestAccelClassic(float range_min = 0, float range_max = BASIC_TEST_RANGE_MAX); 37 | static bool TestAccelMotivity(float range_min = 0, float range_max = BASIC_TEST_RANGE_MAX); 38 | static bool TestAccelSynchronous(float range_min, float range_max); 39 | static bool TestAccelNatural(float range_min = 0, float range_max = BASIC_TEST_RANGE_MAX); 40 | static bool TestAccelJump(float range_min = 0, float range_max = BASIC_TEST_RANGE_MAX); 41 | static bool TestAccelLUT(float range_min = 0, float range_max = BASIC_TEST_RANGE_MAX); 42 | 43 | static bool TestAccelMode(AccelMode mode, float range_min = 0, float range_max = BASIC_TEST_RANGE_MAX); 44 | 45 | static std::array TestAllBasic(float range_min = 0, float range_max = BASIC_TEST_RANGE_MAX); 46 | 47 | static bool TestFixedPointArithmetic(); 48 | 49 | private: 50 | //static CachedFunction functions[AccelMode_Count]; 51 | 52 | class TestSupervisor { 53 | bool _result = true; 54 | int test_idx = 1; 55 | const char *test_name; 56 | std::chrono::steady_clock::time_point start_time; 57 | 58 | public: 59 | explicit TestSupervisor(const char *test_name) : test_name(test_name) { 60 | }; 61 | bool GetResult() const { return _result & result; } 62 | 63 | void Validate(bool res); // EXPECT_TRUE 64 | 65 | bool result = true; 66 | 67 | void NextTest(); 68 | 69 | ~TestSupervisor(); 70 | 71 | private: 72 | void TestPass(); 73 | }; 74 | 75 | static bool IsAccelValueGood(FP_LONG value); 76 | static bool IsCloseEnough(FP_LONG value1, float value2, float tolerance = 0.001f); 77 | static bool IsCloseEnoughRelative(FP_LONG value1, float value2, float tolerance = 0.00001f); 78 | 79 | static float lerp(float a, float b, float t); 80 | }; 81 | 82 | 83 | #endif //TESTS_H 84 | -------------------------------------------------------------------------------- /tests/driver/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef CONFIG 4 | #define CONFIG 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #define printk printf 12 | 13 | #include "FixedMath/Fixed64.h" 14 | static float FP64_ToFloat(FP_LONG v) { 15 | return (float) v * (1.0f / 4294967296.0f); 16 | } 17 | 18 | #ifndef MIN 19 | #define MIN(x, y) (((x) < (y)) ? (x) : (y)) 20 | #endif 21 | 22 | 23 | 24 | #endif -------------------------------------------------------------------------------- /tests/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "TestManager.h" 4 | #include "Tests.h" 5 | 6 | int main() { 7 | Tests::Initialize(); 8 | TestManager::Initialize(); 9 | 10 | auto basic_test_res = Tests::TestAllBasic(); 11 | 12 | int bad_sum = 0; 13 | for (int i = 0; i < basic_test_res.size(); i++) { 14 | if (!basic_test_res[i]) { 15 | fprintf(stderr, "Test failed for '%s' mode\n", AccelMode2String(static_cast(i)).c_str()); 16 | bad_sum++; 17 | } 18 | } 19 | 20 | bool arithmetic_test = Tests::TestFixedPointArithmetic(); 21 | 22 | if (bad_sum == 0 && arithmetic_test) { 23 | printf(GREEN"All tests passed!\n\n" RESET); 24 | } else { 25 | printf(RED"%i %s failed!\n\n", bad_sum, (bad_sum == 1) ? "test" : "tests"); 26 | } 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Get the installed version of the driver 4 | installed_version=$(dkms status -k $(uname -r) | grep -oP '^([l|y]eetmouse-driver[\/(, )]) ?\K([0-9.]+)') 5 | 6 | # Check if the driver is even installed 7 | if [[ -z "$installed_version" ]]; then 8 | echo "Driver not installed, exiting." 9 | exit 0 10 | fi 11 | 12 | # Check if the installed version is old 13 | if [[ $(printf '%s\n' "$installed_version" "0.9.1" | sort -V | head -n1) == "$installed_version" ]]; then 14 | echo "Please uninstall the old driver ($installed_version) using the old uninstaller first!" 15 | exit 1 16 | fi 17 | 18 | # Uninstall the driver 19 | #sudo dkms remove -m yeetmouse-driver -v $installed_version # Newer dkms versions 20 | sudo dkms remove "yeetmouse-driver/$installed_version" --all # Older (for Ubuntu <= 20.04), but should work now too 21 | sudo make remove_dkms 22 | sudo modprobe -r yeetmouse 23 | sudo rmmod yeetmouse 24 | --------------------------------------------------------------------------------