├── .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 | 
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 | 
174 | > Log values scale
175 |
176 | 
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 | 
185 | 
186 | 
187 | 
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 | 
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 | 
202 | 
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 | 
210 | 
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 | 
223 | 
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 | 
231 | 
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 | 
240 | 
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 | | Instruction |
129 | Fixed-Point / FPU |
130 | Mop/s |
131 | ns/op |
132 | Clock cycles/op |
133 |
134 |
135 |
136 | | Multiplication |
137 | Fixed-Point 64 |
138 | 542.905367 |
139 | 1.911 |
140 | 7.029038 |
141 |
142 |
143 | | Fixed-Point 64 (128bit) |
144 | 540.682695 |
145 | 1.913 |
146 | 7.012462 |
147 |
148 |
149 | | FPU (double) |
150 | 788.524105 |
151 | 1.29 |
152 | 4.722532 |
153 |
154 |
155 | | Division |
156 | Fixed-Point 64 (Precise) |
157 | 91.446419 |
158 | 11.299 |
159 | 41.756461 |
160 |
161 |
162 | | Fixed-Point 64 (128bit) |
163 | 203.819151 |
164 | 5.097 |
165 | 18.797924 |
166 |
167 |
168 | | FPU (double) |
169 | 188.035704 |
170 | 5.392 |
171 | 19.879064 |
172 |
173 |
174 | | Exponent |
175 | Fixed-Point 64 |
176 | 66.550845 |
177 | 15.561 |
178 | 57.525454 |
179 |
180 |
181 | | Fixed-Point 64 (Fast) |
182 | 92.775366 |
183 | 11.285 |
184 | 41.702182 |
185 |
186 |
187 | | FPU (double) |
188 | 116.396443 |
189 | 8.741 |
190 | 32.276506 |
191 |
192 |
193 | | Sqrt |
194 | Fixed-Point 64 (Precise) |
195 | 18.059895 |
196 | 57.307 |
197 | 211.97892 |
198 |
199 |
200 | | Fixed-Point 64 |
201 | 64.558792 |
202 | 15.675 |
203 | 57.956097 |
204 |
205 |
206 | | FPU (double) |
207 | 133.474534 |
208 | 7.9 |
209 | 29.179384 |
210 |
211 |
212 | | Pow |
213 | Fixed-Point 64 |
214 | 31.81294 |
215 | 32.221 |
216 | 119.111214 |
217 |
218 |
219 | | Fixed-Point 64 (Fast) |
220 | 40.524527 |
221 | 26.043 |
222 | 96.310556 |
223 |
224 |
225 | | FPU (double) |
226 | 77.804544 |
227 | 17.113 |
228 | 63.251944 |
229 |
230 |
231 | | Log |
232 | Fixed-Point 64 |
233 | 51.117073 |
234 | 21.033 |
235 | 77.768302 |
236 |
237 |
238 | | Fixed-Point 64 (Fast) |
239 | 61.341951 |
240 | 16.638 |
241 | 61.497848 |
242 |
243 |
244 | | FPU (double) |
245 | 53.326065 |
246 | 19.876 |
247 | 73.491065 |
248 |
249 |
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 |
--------------------------------------------------------------------------------