diff --git a/keyboards/bastardkb/charybdis/3x5/keymaps/vendor/keymap.c b/keyboards/bastardkb/charybdis/3x5/keymaps/vendor/keymap.c index 30fa3d9f4fe..f2c3c9b042b 100644 --- a/keyboards/bastardkb/charybdis/3x5/keymaps/vendor/keymap.c +++ b/keyboards/bastardkb/charybdis/3x5/keymaps/vendor/keymap.c @@ -19,6 +19,9 @@ #ifdef CHARYBDIS_AUTO_POINTER_LAYER_TRIGGER_ENABLE # include "timer.h" #endif // CHARYBDIS_AUTO_POINTER_LAYER_TRIGGER_ENABLE +#ifdef MACCEL_ENABLE + #include "features/maccel/maccel.h" +#endif enum charybdis_keymap_layers { LAYER_BASE = 0, @@ -216,8 +219,8 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { // clang-format on #ifdef POINTING_DEVICE_ENABLE -# ifdef CHARYBDIS_AUTO_POINTER_LAYER_TRIGGER_ENABLE report_mouse_t pointing_device_task_user(report_mouse_t mouse_report) { +# ifdef CHARYBDIS_AUTO_POINTER_LAYER_TRIGGER_ENABLE if (abs(mouse_report.x) > CHARYBDIS_AUTO_POINTER_LAYER_TRIGGER_THRESHOLD || abs(mouse_report.y) > CHARYBDIS_AUTO_POINTER_LAYER_TRIGGER_THRESHOLD) { if (auto_pointer_layer_timer == 0) { layer_on(LAYER_POINTER); @@ -228,10 +231,15 @@ report_mouse_t pointing_device_task_user(report_mouse_t mouse_report) { } auto_pointer_layer_timer = timer_read(); } +# endif // CHARYBDIS_AUTO_POINTER_LAYER_TRIGGER_ENABLE +#ifdef MACCEL_ENABLE + mouse_report = pointing_device_task_maccel(mouse_report); +#endif return mouse_report; } void matrix_scan_user(void) { +# ifdef CHARYBDIS_AUTO_POINTER_LAYER_TRIGGER_ENABLE if (auto_pointer_layer_timer != 0 && TIMER_DIFF_16(timer_read(), auto_pointer_layer_timer) >= CHARYBDIS_AUTO_POINTER_LAYER_TRIGGER_TIMEOUT_MS) { auto_pointer_layer_timer = 0; layer_off(LAYER_POINTER); @@ -239,8 +247,8 @@ void matrix_scan_user(void) { rgb_matrix_mode_noeeprom(RGB_MATRIX_DEFAULT_MODE); # endif // RGB_MATRIX_ENABLE } -} # endif // CHARYBDIS_AUTO_POINTER_LAYER_TRIGGER_ENABLE +} # ifdef CHARYBDIS_AUTO_SNIPING_ON_LAYER layer_state_t layer_state_set_user(layer_state_t state) { diff --git a/keyboards/bastardkb/charybdis/3x5/keymaps/vendor/rules.mk b/keyboards/bastardkb/charybdis/3x5/keymaps/vendor/rules.mk index 1e5b99807cb..4dea2893b28 100644 --- a/keyboards/bastardkb/charybdis/3x5/keymaps/vendor/rules.mk +++ b/keyboards/bastardkb/charybdis/3x5/keymaps/vendor/rules.mk @@ -1 +1,5 @@ VIA_ENABLE = yes + +USER_NAME := bastardkb + +MACCEL_ENABLE = yes diff --git a/users/bastardkb/features/maccel/assets/accel_curve.png b/users/bastardkb/features/maccel/assets/accel_curve.png new file mode 100644 index 00000000000..57ab94df65d Binary files /dev/null and b/users/bastardkb/features/maccel/assets/accel_curve.png differ diff --git a/users/bastardkb/features/maccel/assets/charybdis_3x5.json b/users/bastardkb/features/maccel/assets/charybdis_3x5.json new file mode 100644 index 00000000000..a3b61d96b21 --- /dev/null +++ b/users/bastardkb/features/maccel/assets/charybdis_3x5.json @@ -0,0 +1,187 @@ +{ + "name": "Charybdis Nano", + "vendorId": "0xA8F8", + "productId": "0x1832", + "menus": [ + "qmk_rgb_matrix", + { + "label": "Mouse acceleration", + "content": [ + { + "label": "Acceleration", + "content": [ + { + "label": "Enabled", + "type": "toggle", + "content": [ + "id_maccel_enabled", + 24, + 5 + ] + }, + { + "label": "Takeoff", + "type": "range", + "options": [ + 0, + 60000 + ], + "content": [ + "id_maccel_takeoff", + 24, + 1 + ] + }, + { + "label": "Growth Rate", + "type": "range", + "options": [ + 0, + 60000 + ], + "content": [ + "id_maccel_growth_rate", + 24, + 2 + ] + }, + { + "label": "Offset", + "type": "range", + "options": [ + 0, + 60000 + ], + "content": [ + "id_maccel_offset", + 24, + 3 + ] + }, + { + "label": "Limit", + "type": "range", + "options": [ + 0, + 60000 + ], + "content": [ + "id_maccel_limit", + 24, + 4 + ] + } + ] + } + ] + } + ], + "matrix": { + "rows": 8, + "cols": 5 + }, + "customKeycodes": [ + { + "title": "Default DPI Increase", + "name": "Default DPI Inc", + "shortName": "DPI+" + }, + { + "title": "Default DPI Decrease", + "name": "Default DPI Dec", + "shortName": "DPI-" + }, + { + "title": "Sniping DPI Increase", + "name": "Sniping DPI Inc", + "shortName": "Snp+" + }, + { + "title": "Sniping DPI Decrease", + "name": "Sniping DPI Dec", + "shortName": "Snp-" + }, + { + "title": "Sniping Momentary", + "name": "Sniping MO", + "shortName": "Snp" + }, + { + "title": "Sniping Toggle", + "name": "Sniping TO", + "shortName": "SnpT" + }, + { + "title": "Drag-scroll Momentary", + "name": "Drag-scroll MO", + "shortName": "Drg" + }, + { + "title": "Drag-scroll Toggle", + "name": "Drag-scroll TG", + "shortName": "DrgT" + } + ], + "layouts": { + "keymap": [ + [ + "0,0", + "0,1", + "0,2", + "0,3", + "0,4", + { + "x": 4 + }, + "4,4", + "4,3", + "4,2", + "4,1", + "4,0" + ], + [ + "1,0", + "1,1", + "1,2", + "1,3", + "1,4", + { + "x": 4 + }, + "5,4", + "5,3", + "5,2", + "5,1", + "5,0" + ], + [ + "2,0", + "2,1", + "2,2", + "2,3", + "2,4", + { + "x": 4 + }, + "6,4", + "6,3", + "6,2", + "6,1", + "6,0" + ], + [ + { + "x": 3 + }, + "3,2", + "3,3", + "3,0", + { + "x": 2 + }, + "7,0", + "7,2" + ] + ] + } +} diff --git a/users/bastardkb/features/maccel/assets/charybdis_3x6.json b/users/bastardkb/features/maccel/assets/charybdis_3x6.json new file mode 100644 index 00000000000..5b86bc86046 --- /dev/null +++ b/users/bastardkb/features/maccel/assets/charybdis_3x6.json @@ -0,0 +1,331 @@ +{ + "name": "Charybdis Mini", + "vendorId": "0xA8F8", + "productId": "0x1834", + "matrix": { + "rows": 8, + "cols": 6 + }, + "menus": [ + "qmk_rgb_matrix", + { + "label": "Mouse acceleration", + "content": [ + { + "label": "Acceleration", + "content": [ + { + "label": "Enabled", + "type": "toggle", + "content": [ + "id_maccel_enabled", + 24, + 5 + ] + }, + { + "label": "Takeoff", + "type": "range", + "options": [ + 0, + 60000 + ], + "content": [ + "id_maccel_takeoff", + 24, + 1 + ] + }, + { + "label": "Growth Rate", + "type": "range", + "options": [ + 0, + 60000 + ], + "content": [ + "id_maccel_growth_rate", + 24, + 2 + ] + }, + { + "label": "Offset", + "type": "range", + "options": [ + 0, + 60000 + ], + "content": [ + "id_maccel_offset", + 24, + 3 + ] + }, + { + "label": "Limit", + "type": "range", + "options": [ + 0, + 60000 + ], + "content": [ + "id_maccel_limit", + 24, + 4 + ] + } + ] + } + ] + } + ], + "keycodes": [ + "qmk_lighting" + ], + "customKeycodes": [ + { + "name": "Default DPI Forward", + "title": "Default DPI Forward", + "shortName": "DPI+" + }, + { + "name": "Default DPI Reverse", + "title": "Default DPI Reverse", + "shortName": "DPI-" + }, + { + "name": "Sniping DPI Forward", + "title": "Sniping DPI Forward", + "shortName": "Sniping+" + }, + { + "name": "Sniping DPI Reverse", + "title": "Sniping DPI Reverse", + "shortName": "Sniping-" + }, + { + "name": "Sniping Mode", + "title": "Sniping Mode", + "shortName": "SnipeMO" + }, + { + "name": "Sniping Toggle", + "title": "Sniping Toggle", + "shortName": "SnipeTG" + }, + { + "name": "Dragscroll Mode", + "title": "Dragscroll Mode", + "shortName": "DragScl" + }, + { + "name": "Dragscroll Toggle", + "title": "Dragscroll Toggle", + "shortName": "DragTog" + } + ], + "layouts": { + "labels": [ + [ + "Trackball", + "Right", + "Left" + ] + ], + "keymap": [ + [ + { + "x": 3 + }, + "0,3", + { + "x": 8 + }, + "4,3" + ], + [ + { + "y": -0.9, + "x": 2 + }, + "0,2" + ], + [ + { + "y": -1, + "x": 4 + }, + "0,4", + { + "x": 6 + }, + "4,4", + { + "x": 1 + }, + "4,2" + ], + [ + { + "y": -0.98, + "x": 5 + }, + "0,5", + { + "x": 4 + }, + "4,5" + ], + [ + { + "y": -0.62 + }, + "0,0", + "0,1", + { + "x": 12 + }, + "4,1", + "4,0" + ], + [ + { + "y": -0.5, + "x": 3 + }, + "1,3", + { + "x": 8 + }, + "5,3" + ], + [ + { + "y": -0.9, + "x": 2 + }, + "1,2", + { + "x": 1 + }, + "1,4", + { + "x": 6 + }, + "5,4", + { + "x": 1 + }, + "5,2" + ], + [ + { + "y": -0.98, + "x": 5 + }, + "1,5", + { + "x": 4 + }, + "5,5" + ], + [ + { + "y": -0.62 + }, + "1,0", + "1,1", + { + "x": 12 + }, + "5,1", + "5,0" + ], + [ + { + "y": -0.5, + "x": 3 + }, + "2,3", + { + "x": 8 + }, + "6,3" + ], + [ + { + "y": -0.9, + "x": 2 + }, + "2,2", + { + "x": 1 + }, + "2,4", + { + "x": 6 + }, + "6,4", + { + "x": 1 + }, + "6,2" + ], + [ + { + "y": -0.98, + "x": 5 + }, + "2,5", + { + "x": 4 + }, + "6,5" + ], + [ + { + "y": -0.62 + }, + "2,0", + "2,1", + { + "x": 12 + }, + "6,1", + "6,0" + ], + [ + { + "x": 4.75 + }, + "3,3\n\n\n0,0", + "3,4\n\n\n0,0", + "3,1", + { + "x": 0.5 + }, + "7,1", + "7,3\n\n\n0,0", + { + "d": true + }, + "\n\n\n0,0" + ], + [ + { + "y": 0.5, + "x": 4.75, + "d": true + }, + "\n\n\n0,1", + "3,3\n\n\n0,1", + { + "x": 2.5 + }, + "7,4\n\n\n0,1", + "7,3\n\n\n0,1" + ] + ] + } +} diff --git a/users/bastardkb/features/maccel/assets/charybdis_4x6.json b/users/bastardkb/features/maccel/assets/charybdis_4x6.json new file mode 100644 index 00000000000..53be2376114 --- /dev/null +++ b/users/bastardkb/features/maccel/assets/charybdis_4x6.json @@ -0,0 +1,221 @@ +{ + "name": "Charybdis", + "vendorId": "0xA8F8", + "productId": "0x1833", + "menus": [ + "qmk_rgb_matrix", + { + "label": "Mouse acceleration", + "content": [ + { + "label": "Acceleration", + "content": [ + { + "label": "Enabled", + "type": "toggle", + "content": [ + "id_maccel_enabled", + 24, + 5 + ] + }, + { + "label": "Takeoff", + "type": "range", + "options": [ + 0, + 60000 + ], + "content": [ + "id_maccel_takeoff", + 24, + 1 + ] + }, + { + "label": "Growth Rate", + "type": "range", + "options": [ + 0, + 60000 + ], + "content": [ + "id_maccel_growth_rate", + 24, + 2 + ] + }, + { + "label": "Offset", + "type": "range", + "options": [ + 0, + 60000 + ], + "content": [ + "id_maccel_offset", + 24, + 3 + ] + }, + { + "label": "Limit", + "type": "range", + "options": [ + 0, + 60000 + ], + "content": [ + "id_maccel_limit", + 24, + 4 + ] + } + ] + } + ] + } + ], + "matrix": { + "rows": 10, + "cols": 6 + }, + "customKeycodes": [ + { + "title": "Default DPI Increase", + "name": "Default DPI Inc", + "shortName": "DPI+" + }, + { + "title": "Default DPI Decrease", + "name": "Default DPI Dec", + "shortName": "DPI-" + }, + { + "title": "Sniping DPI Increase", + "name": "Sniping DPI Inc", + "shortName": "Snp+" + }, + { + "title": "Sniping DPI Decrease", + "name": "Sniping DPI Dec", + "shortName": "Snp-" + }, + { + "title": "Sniping Momentary", + "name": "Sniping MO", + "shortName": "Snp" + }, + { + "title": "Sniping Toggle", + "name": "Sniping TO", + "shortName": "SnpT" + }, + { + "title": "Drag-scroll Momentary", + "name": "Drag-scroll MO", + "shortName": "Drg" + }, + { + "title": "Drag-scroll Toggle", + "name": "Drag-scroll TG", + "shortName": "DrgT" + } + ], + "layouts": { + "keymap": [ + [ + "0,0", + "0,1", + "0,2", + "0,3", + "0,4", + "0,5", + { + "x": 4 + }, + "5,5", + "5,4", + "5,3", + "5,2", + "5,1", + "5,0" + ], + [ + "1,0", + "1,1", + "1,2", + "1,3", + "1,4", + "1,5", + { + "x": 4 + }, + "6,5", + "6,4", + "6,3", + "6,2", + "6,1", + "6,0" + ], + [ + "2,0", + "2,1", + "2,2", + "2,3", + "2,4", + "2,5", + { + "x": 4 + }, + "7,5", + "7,4", + "7,3", + "7,2", + "7,1", + "7,0" + ], + [ + "3,0", + "3,1", + "3,2", + "3,3", + "3,4", + "3,5", + { + "x": 4 + }, + "8,5", + "8,4", + "8,3", + "8,2", + "8,1", + "8,0" + ], + [ + { + "x": 4 + }, + "4,3", + "4,4", + "4,1", + { + "x": 2 + }, + "9,1", + "9,3" + ], + [ + { + "x": 5 + }, + "4,5", + "4,2", + { + "x": 2 + }, + "9,5" + ] + ] + } +} diff --git a/users/bastardkb/features/maccel/assets/dilemma_max.json b/users/bastardkb/features/maccel/assets/dilemma_max.json new file mode 100644 index 00000000000..4b156df6df9 --- /dev/null +++ b/users/bastardkb/features/maccel/assets/dilemma_max.json @@ -0,0 +1,437 @@ +{ + "name": "Dilemma Max", + "vendorId": "0xA8F8", + "productId": "0x1837", + "matrix": { + "rows": 10, + "cols": 6 + }, + "menus": [ + "qmk_rgb_matrix", + { + "label": "Mouse acceleration", + "content": [ + { + "label": "Acceleration", + "content": [ + { + "label": "Enabled", + "type": "toggle", + "content": [ + "id_maccel_enabled", + 24, + 5 + ] + }, + { + "label": "Takeoff", + "type": "range", + "options": [ + 0, + 60000 + ], + "content": [ + "id_maccel_takeoff", + 24, + 1 + ] + }, + { + "label": "Growth Rate", + "type": "range", + "options": [ + 0, + 60000 + ], + "content": [ + "id_maccel_growth_rate", + 24, + 2 + ] + }, + { + "label": "Offset", + "type": "range", + "options": [ + 0, + 60000 + ], + "content": [ + "id_maccel_offset", + 24, + 3 + ] + }, + { + "label": "Limit", + "type": "range", + "options": [ + 0, + 60000 + ], + "content": [ + "id_maccel_limit", + 24, + 4 + ] + } + ] + } + ] + } + ], + "keycodes": [ + "qmk_lighting" + ], + "customKeycodes": [ + { + "name": "Default DPI Forward", + "title": "Default DPI Forward", + "shortName": "DPI+" + }, + { + "name": "Default DPI Reverse", + "title": "Default DPI Reverse", + "shortName": "DPI-" + }, + { + "name": "Sniping DPI Forward", + "title": "Sniping DPI Forward", + "shortName": "Sniping+" + }, + { + "name": "Sniping DPI Reverse", + "title": "Sniping DPI Reverse", + "shortName": "Sniping-" + }, + { + "name": "Sniping Mode", + "title": "Sniping Mode", + "shortName": "SnipeMO" + }, + { + "name": "Sniping Toggle", + "title": "Sniping Toggle", + "shortName": "SnipeTG" + }, + { + "name": "Dragscroll Mode", + "title": "Dragscroll Mode", + "shortName": "DragScl" + }, + { + "name": "Dragscroll Toggle", + "title": "Dragscroll Toggle", + "shortName": "DragTog" + } + ], + "layouts": { + "labels": [ + "Left Encoder", + "Right Encoder" + ], + "keymap": [ + [ + { + "x": 3 + }, + "0,3", + { + "x": 7.5 + }, + "5,3" + ], + [ + { + "y": -0.8, + "x": 4 + }, + "0,4" + ], + [ + { + "y": -1, + "x": 10.5 + }, + "5,4" + ], + [ + { + "y": -0.9, + "x": 2 + }, + "0,2" + ], + [ + { + "y": -1, + "x": 5 + }, + "0,5" + ], + [ + { + "y": -1, + "x": 9.5 + }, + "5,5" + ], + [ + { + "y": -1, + "x": 12.5 + }, + "5,2" + ], + [ + { + "y": -0.4 + }, + "0,0", + "0,1", + { + "x": 11.5 + }, + "5,1", + "5,0" + ], + [ + { + "y": -0.9, + "x": 3 + }, + "1,3", + { + "x": 7.5 + }, + "6,3" + ], + [ + { + "y": -0.8, + "x": 4 + }, + "1,4", + { + "x": 5.5 + }, + "6,4" + ], + [ + { + "y": -0.9, + "x": 2 + }, + "1,2", + { + "x": 2 + }, + "1,5", + { + "x": 3.5 + }, + "6,5", + { + "x": 2 + }, + "6,2" + ], + [ + { + "y": -0.4 + }, + "1,0", + "1,1", + { + "x": 11.5 + }, + "6,1", + "6,0" + ], + [ + { + "y": -0.9, + "x": 3 + }, + "2,3", + { + "x": 7.5 + }, + "7,3" + ], + [ + { + "y": -0.8, + "x": 4 + }, + "2,4", + { + "x": 5.5 + }, + "7,4" + ], + [ + { + "y": -0.9, + "x": 2 + }, + "2,2", + { + "x": 2 + }, + "2,5", + { + "x": 3.5 + }, + "7,5", + { + "x": 2 + }, + "7,2" + ], + [ + { + "y": -0.4 + }, + "2,0", + "2,1", + { + "x": 11.5 + }, + "7,1", + "7,0" + ], + [ + { + "y": -0.9, + "x": 3 + }, + "3,3", + { + "x": 7.5 + }, + "8,3" + ], + [ + { + "y": -0.8, + "x": 4 + }, + "3,4", + { + "x": 5.5 + }, + "8,4" + ], + [ + { + "y": -0.9, + "x": 2 + }, + "3,2", + { + "x": 2 + }, + "3,5", + { + "x": 3.5 + }, + "8,5", + { + "x": 2 + }, + "8,2" + ], + [ + { + "y": -0.4 + }, + "3,0", + "3,1", + { + "x": 11.5 + }, + "8,1", + "8,0" + ], + [ + { + "y": 0.5, + "x": 3.3 + }, + "4,3\n\n\n0,1\n\n\n\n\n\ne0", + { + "x": 6.9 + }, + "9,3\n\n\n1,1\n\n\n\n\n\ne1" + ], + [ + { + "rx": 3.9, + "ry": 9.175, + "y": -4.875, + "x": -0.6 + }, + "4,3\n\n\n0,0" + ], + [ + { + "rx": 11.6, + "y": -4.875, + "x": -0.4 + }, + "9,3\n\n\n1,0" + ], + [ + { + "r": 15, + "rx": 3.9, + "y": -4.875, + "x": -0.6 + }, + "4,1" + ], + [ + { + "r": 30, + "y": -1, + "x": -0.6 + }, + "4,2" + ], + [ + { + "r": 45, + "y": -1, + "x": -0.6 + }, + "4,4" + ], + [ + { + "r": -45, + "rx": 11.6, + "y": -4.875, + "x": -0.4 + }, + "9,4" + ], + [ + { + "r": -30, + "y": -1, + "x": -0.4 + }, + "9,2" + ], + [ + { + "r": -15, + "y": -1, + "x": -0.4 + }, + "9,1" + ] + ] + } +} diff --git a/users/bastardkb/features/maccel/assets/dilemma_v2.json b/users/bastardkb/features/maccel/assets/dilemma_v2.json new file mode 100644 index 00000000000..2abf3acfd6e --- /dev/null +++ b/users/bastardkb/features/maccel/assets/dilemma_v2.json @@ -0,0 +1,362 @@ +{ + "name": "Dilemma v2", + "vendorId": "0xA8F8", + "productId": "0x1836", + "menus": [ + "qmk_rgb_matrix", + { + "label": "Mouse acceleration", + "content": [ + { + "label": "Acceleration", + "content": [ + { + "label": "Enabled", + "type": "toggle", + "content": [ + "id_maccel_enabled", + 24, + 5 + ] + }, + { + "label": "Takeoff", + "type": "range", + "options": [ + 0, + 60000 + ], + "content": [ + "id_maccel_takeoff", + 24, + 1 + ] + }, + { + "label": "Growth Rate", + "type": "range", + "options": [ + 0, + 60000 + ], + "content": [ + "id_maccel_growth_rate", + 24, + 2 + ] + }, + { + "label": "Offset", + "type": "range", + "options": [ + 0, + 60000 + ], + "content": [ + "id_maccel_offset", + 24, + 3 + ] + }, + { + "label": "Limit", + "type": "range", + "options": [ + 0, + 60000 + ], + "content": [ + "id_maccel_limit", + 24, + 4 + ] + } + ] + } + ] + } + ], + "keycodes": [ + "qmk_lighting" + ], + "matrix": { + "rows": 8, + "cols": 5 + }, + "customKeycodes": [ + { + "title": "Default DPI Increase", + "name": "Default DPI Inc", + "shortName": "DPI+" + }, + { + "title": "Default DPI Decrease", + "name": "Default DPI Dec", + "shortName": "DPI-" + }, + { + "title": "Sniping DPI Increase", + "name": "Sniping DPI Inc", + "shortName": "Snp+" + }, + { + "title": "Sniping DPI Decrease", + "name": "Sniping DPI Dec", + "shortName": "Snp-" + }, + { + "title": "Sniping Momentary", + "name": "Sniping MO", + "shortName": "Snp" + }, + { + "title": "Sniping Toggle", + "name": "Sniping TO", + "shortName": "SnpT" + }, + { + "title": "Drag-scroll Momentary", + "name": "Drag-scroll MO", + "shortName": "Drg" + }, + { + "title": "Drag-scroll Toggle", + "name": "Drag-scroll TG", + "shortName": "DrgT" + } + ], + "layouts": { + "labels": [ + "Left Encoder", + "Right Encoder" + ], + "keymap": [ + [ + { + "x": 2 + }, + "0,2", + { + "x": 7 + }, + "4,2" + ], + [ + { + "y": -0.8, + "x": 3 + }, + "0,3" + ], + [ + { + "y": -1, + "x": 9 + }, + "4,3" + ], + [ + { + "y": -0.95, + "x": 4 + }, + "0,4", + { + "x": 3 + }, + "4,4" + ], + [ + { + "y": -0.85, + "x": 1 + }, + "0,1", + { + "x": 9 + }, + "4,1" + ], + [ + { + "y": -0.5999999999999999 + }, + "0,0", + { + "x": 11 + }, + "4,0" + ], + [ + { + "y": -0.8, + "x": 2 + }, + "1,2", + { + "x": 7 + }, + "5,2" + ], + [ + { + "y": -0.8, + "x": 3 + }, + "1,3", + { + "x": 5 + }, + "5,3" + ], + [ + { + "y": -0.9500000000000002, + "x": 4 + }, + "1,4", + { + "x": 3 + }, + "5,4" + ], + [ + { + "y": -0.8500000000000001, + "x": 1 + }, + "1,1", + { + "x": 9 + }, + "5,1" + ], + [ + { + "y": -0.5999999999999999 + }, + "1,0", + { + "x": 11 + }, + "5,0" + ], + [ + { + "y": -0.7999999999999998, + "x": 2 + }, + "2,2", + { + "x": 7 + }, + "6,2" + ], + [ + { + "y": -0.7999999999999998, + "x": 3 + }, + "2,3", + { + "x": 5 + }, + "6,3" + ], + [ + { + "y": -0.9500000000000002, + "x": 4 + }, + "2,4", + { + "x": 3 + }, + "6,4" + ], + [ + { + "y": -0.8500000000000001, + "x": 1 + }, + "2,1", + { + "x": 9 + }, + "6,1" + ], + [ + { + "y": -0.6000000000000001 + }, + "2,0", + { + "x": 11 + }, + "6,0" + ], + [ + { + "y": 0.6000000000000005, + "x": 2.5 + }, + "3,2\n\n\n0,1\n\n\n\n\n\ne0", + { + "x": 6 + }, + "7,2\n\n\n1,1\n\n\n\n\n\ne1" + ], + [ + { + "rx": 2.9, + "ry": 8.175, + "y": -4.875000000000001, + "x": -0.6000000000000001 + }, + "3,2\n\n\n0,0" + ], + [ + { + "rx": 10.2, + "y": -4.875000000000001, + "x": -0.5 + }, + "7,2\n\n\n1,0" + ], + [ + { + "r": 15, + "rx": 2.9, + "y": -4.875000000000001, + "x": -0.6000000000000001 + }, + "3,0" + ], + [ + { + "r": 30, + "y": -1, + "x": -0.6000000000000001 + }, + "3,1" + ], + [ + { + "r": -30, + "rx": 10.2, + "y": -4.875000000000001, + "x": -0.5 + }, + "7,1" + ], + [ + { + "r": -15, + "y": -1, + "x": -0.5 + }, + "7,0" + ] + ] + } +} diff --git a/users/bastardkb/features/maccel/assets/via.json b/users/bastardkb/features/maccel/assets/via.json new file mode 100644 index 00000000000..9536c4da44f --- /dev/null +++ b/users/bastardkb/features/maccel/assets/via.json @@ -0,0 +1,71 @@ + { + "label": "Mouse acceleration", + "content": [ + { + "label": "Acceleration", + "content": [ + { + "label": "Enabled", + "type": "toggle", + "content": [ + "id_maccel_enabled", + 24, + 5 + ] + }, + { + "label": "Takeoff", + "type": "range", + "options": [ + 0, + 60000 + ], + "content": [ + "id_maccel_takeoff", + 24, + 1 + ] + }, + { + "label": "Growth Rate", + "type": "range", + "options": [ + 0, + 60000 + ], + "content": [ + "id_maccel_growth_rate", + 24, + 2 + ] + }, + { + "label": "Offset", + "type": "range", + "options": [ + 0, + 60000 + ], + "content": [ + "id_maccel_offset", + 24, + 3 + ] + }, + { + "label": "Limit", + "type": "range", + "options": [ + 0, + 60000 + ], + "content": [ + "id_maccel_limit", + 24, + 4 + ] + } + ] + } + ] + } diff --git a/users/bastardkb/features/maccel/assets/via.png b/users/bastardkb/features/maccel/assets/via.png new file mode 100644 index 00000000000..ef6f94f4d29 Binary files /dev/null and b/users/bastardkb/features/maccel/assets/via.png differ diff --git a/users/bastardkb/features/maccel/maccel.c b/users/bastardkb/features/maccel/maccel.c new file mode 100644 index 00000000000..af5bf2d8b92 --- /dev/null +++ b/users/bastardkb/features/maccel/maccel.c @@ -0,0 +1,221 @@ +// Copyright 2024 burkfers (@burkfers) +// Copyright 2024 Wimads (@wimads) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "quantum.h" // IWYU pragma: keep +#include "maccel.h" +#include "math.h" + +static uint32_t maccel_timer; + +#ifndef MACCEL_TAKEOFF +# define MACCEL_TAKEOFF 2.0 // lower/higher value = curve starts more smoothly/abruptly +#endif +#ifndef MACCEL_GROWTH_RATE +# define MACCEL_GROWTH_RATE 0.25 // lower/higher value = curve reaches its upper limit slower/faster +#endif +#ifndef MACCEL_OFFSET +# define MACCEL_OFFSET 2.2 // lower/higher value = acceleration kicks in earlier/later +#endif +#ifndef MACCEL_LIMIT +# define MACCEL_LIMIT 0.2 // lower limit of accel curve (minimum acceleration factor) +#endif +#ifndef MACCEL_CPI_THROTTLE_MS +# define MACCEL_CPI_THROTTLE_MS 200 // milliseconds to wait between requesting the device's current DPI +#endif +#ifndef MACCEL_LIMIT_UPPER +# define MACCEL_LIMIT_UPPER 1 // upper limit of accel curve, recommended to leave at 1; adjust DPI setting instead. +#endif +#ifndef MACCEL_ROUNDING_CARRY_TIMEOUT_MS +# define MACCEL_ROUNDING_CARRY_TIMEOUT_MS 200 // milliseconds after which to reset quantization error correction (forget rounding remainder) +#endif + +maccel_config_t g_maccel_config = { + // clang-format off + .growth_rate = MACCEL_GROWTH_RATE, + .offset = MACCEL_OFFSET, + .limit = MACCEL_LIMIT, + .takeoff = MACCEL_TAKEOFF, + .enabled = true + // clang-format on +}; + +#ifdef MACCEL_USE_KEYCODES +# ifndef MACCEL_TAKEOFF_STEP +# define MACCEL_TAKEOFF_STEP 0.01f +# endif +# ifndef MACCEL_GROWTH_RATE_STEP +# define MACCEL_GROWTH_RATE_STEP 0.01f +# endif +# ifndef MACCEL_OFFSET_STEP +# define MACCEL_OFFSET_STEP 0.1f +# endif +# ifndef MACCEL_LIMIT_STEP +# define MACCEL_LIMIT_STEP 0.01f +# endif +#endif + +float maccel_get_takeoff(void) { + return g_maccel_config.takeoff; +} +float maccel_get_growth_rate(void) { + return g_maccel_config.growth_rate; +} +float maccel_get_offset(void) { + return g_maccel_config.offset; +} +float maccel_get_limit(void) { + return g_maccel_config.limit; +} +void maccel_set_takeoff(float val) { + if (val >= 0.5) { // value less than 0.5 leads to nonsensical results + g_maccel_config.takeoff = val; + } +} +void maccel_set_growth_rate(float val) { + if (val >= 0) { // value less 0 leads to nonsensical results + g_maccel_config.growth_rate = val; + } +} +void maccel_set_offset(float val) { + g_maccel_config.offset = val; +} +void maccel_set_limit(float val) { + if (val >= 0) { + g_maccel_config.limit = val; + } +} + +void maccel_enabled(bool enable) { + g_maccel_config.enabled = enable; +#ifdef MACCEL_DEBUG + printf("maccel: enabled: %d\n", g_maccel_config.enabled); +#endif +} +bool maccel_get_enabled(void) { + return g_maccel_config.enabled; +} +void maccel_toggle_enabled(void) { + maccel_enabled(!maccel_get_enabled()); +} + +#define _CONSTRAIN(amt, low, high) ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt))) +#define CONSTRAIN_REPORT(val) (mouse_xy_report_t) _CONSTRAIN(val, XY_REPORT_MIN, XY_REPORT_MAX) + +report_mouse_t pointing_device_task_maccel(report_mouse_t mouse_report) { + // rounding carry to recycle dropped floats from int mouse reports, to smoothen low speed movements (credit @ankostis) + static float rounding_carry_x = 0; + static float rounding_carry_y = 0; + // time since last mouse report: + const uint16_t delta_time = timer_elapsed32(maccel_timer); + // skip maccel maths if report = 0, or if maccel not enabled. + if ((mouse_report.x == 0 && mouse_report.y == 0) || !g_maccel_config.enabled) { + return mouse_report; + } + // reset timer: + maccel_timer = timer_read32(); + // Reset carry if too much time passed + if (delta_time > MACCEL_ROUNDING_CARRY_TIMEOUT_MS) { + rounding_carry_x = 0; + rounding_carry_y = 0; + } + // Reset carry when pointer swaps direction, to follow user's hand. + if (mouse_report.x * rounding_carry_x < 0) rounding_carry_x = 0; + if (mouse_report.y * rounding_carry_y < 0) rounding_carry_y = 0; + // Limit expensive calls to get device cpi settings only when mouse stationary for > 200ms. + static uint16_t device_cpi = 300; + if (delta_time > MACCEL_CPI_THROTTLE_MS) { + device_cpi = pointing_device_get_cpi(); + } + // calculate dpi correction factor (for normalizing velocity range across different user dpi settings) + const float dpi_correction = (float)1000.0f / device_cpi; + // calculate euclidean distance moved (sqrt(x^2 + y^2)) + const float distance = sqrtf(mouse_report.x * mouse_report.x + mouse_report.y * mouse_report.y); + // calculate delta velocity: dv = distance/dt + const float velocity_raw = distance / delta_time; + // correct raw velocity for dpi + const float velocity = dpi_correction * velocity_raw; + // letter variables for readability of maths: + const float k = g_maccel_config.takeoff; + const float g = g_maccel_config.growth_rate; + const float s = g_maccel_config.offset; + const float m = g_maccel_config.limit; + // acceleration factor: f(v) = 1 - (1 - M) / {1 + e^[K(v - S)]}^(G/K): + // Generalised Sigmoid Function, see https://www.desmos.com/calculator/k9vr0y2gev + const float maccel_factor = MACCEL_LIMIT_UPPER - (MACCEL_LIMIT_UPPER - m) / powf(1 + expf(k * (velocity - s)), g / k); + // multiply mouse reports by acceleration factor, and account for previous quantization errors: + const float new_x = rounding_carry_x + maccel_factor * mouse_report.x; + const float new_y = rounding_carry_y + maccel_factor * mouse_report.y; + // Accumulate any difference from next integer (quantization). + rounding_carry_x = new_x - (int)new_x; + rounding_carry_y = new_y - (int)new_y; + // clamp values + const mouse_xy_report_t x = CONSTRAIN_REPORT(new_x); + const mouse_xy_report_t y = CONSTRAIN_REPORT(new_y); + +// console output for debugging (enable/disable in config.h) +#ifdef MACCEL_DEBUG + const float distance_out = sqrtf(x * x + y * y); + const float velocity_out = velocity * maccel_factor; + printf("MACCEL: DPI:%4i Tko: %.3f Grw: %.3f Ofs: %.3f Lmt: %.3f | Fct: %.3f v.in: %.3f v.out: %.3f d.in: %3i d.out: %3i\n", device_cpi, g_maccel_config.takeoff, g_maccel_config.growth_rate, g_maccel_config.offset, g_maccel_config.limit, maccel_factor, velocity, velocity_out, CONSTRAIN_REPORT(distance), CONSTRAIN_REPORT(distance_out)); +#endif // MACCEL_DEBUG + + // report back accelerated values + mouse_report.x = x; + mouse_report.y = y; + + return mouse_report; +} + +#ifdef MACCEL_USE_KEYCODES +static inline float get_mod_step(float step) { + const uint8_t mod_mask = get_mods(); + if (mod_mask & MOD_MASK_CTRL) { + step *= 10; // control increases by factor 10 + } + if (mod_mask & MOD_MASK_SHIFT) { + step *= -1; // shift inverts + } + return step; +} + +bool process_record_maccel(uint16_t keycode, keyrecord_t *record, uint16_t toggle, uint16_t takeoff, uint16_t growth_rate, uint16_t offset, uint16_t limit) { + if (record->event.pressed) { + if (keycode == toggle) { + maccel_toggle_enabled(); + return false; + } + if (keycode == takeoff) { + maccel_set_takeoff(maccel_get_takeoff() + get_mod_step(MACCEL_TAKEOFF_STEP)); + printf("MACCEL:keycode: TKO: %.3f gro: %.3f ofs: %.3f lmt: %.3f\n", g_maccel_config.takeoff, g_maccel_config.growth_rate, g_maccel_config.offset, g_maccel_config.limit); + return false; + } + if (keycode == growth_rate) { + maccel_set_growth_rate(maccel_get_growth_rate() + get_mod_step(MACCEL_GROWTH_RATE_STEP)); + printf("MACCEL:keycode: tko: %.3f GRO: %.3f ofs: %.3f lmt: %.3f\n", g_maccel_config.takeoff, g_maccel_config.growth_rate, g_maccel_config.offset, g_maccel_config.limit); + return false; + } + if (keycode == offset) { + maccel_set_offset(maccel_get_offset() + get_mod_step(MACCEL_OFFSET_STEP)); + printf("MACCEL:keycode: tko: %.3f gro: %.3f OFS: %.3f lmt: %.3f\n", g_maccel_config.takeoff, g_maccel_config.growth_rate, g_maccel_config.offset, g_maccel_config.limit); + return false; + } + if (keycode == limit) { + maccel_set_limit(maccel_get_limit() + get_mod_step(MACCEL_LIMIT_STEP)); + printf("MACCEL:keycode: tko: %.3f gro: %.3f ofs: %.3f LMT: %.3f\n", g_maccel_config.takeoff, g_maccel_config.growth_rate, g_maccel_config.offset, g_maccel_config.limit); + return false; + } + } + return true; +} +#else +bool process_record_maccel(uint16_t keycode, keyrecord_t *record, uint16_t toggle, uint16_t takeoff, uint16_t growth_rate, uint16_t offset, uint16_t limit) { + // provide a do-nothing keyrecord function so a user doesn't need to un-shim when disabling the keycodes + return true; +} +#endif + +// provide weak do-nothing shims so users do not need to un-shim when disabling via +__attribute__((weak)) void keyboard_post_init_maccel(void) { + return; +} diff --git a/users/bastardkb/features/maccel/maccel.h b/users/bastardkb/features/maccel/maccel.h new file mode 100644 index 00000000000..cd94ff6c7eb --- /dev/null +++ b/users/bastardkb/features/maccel/maccel.h @@ -0,0 +1,36 @@ +// Copyright 2024 burkfers (@burkfers) +// Copyright 2024 Wimads (@wimads) +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "action.h" +#include "report.h" + +report_mouse_t pointing_device_task_maccel(report_mouse_t mouse_report); +bool process_record_maccel(uint16_t keycode, keyrecord_t *record, uint16_t toggle, uint16_t takeoff, uint16_t growth_rate, uint16_t offset, uint16_t limit); + +typedef struct _maccel_config_t { + float growth_rate; + float offset; + float limit; + float takeoff; + bool enabled; +} maccel_config_t; + +extern maccel_config_t g_maccel_config; + +void maccel_enabled(bool enable); +bool maccel_get_enabled(void); +void maccel_toggle_enabled(void); + +float maccel_get_takeoff(void); +float maccel_get_growth_rate(void); +float maccel_get_offset(void); +float maccel_get_limit(void); +void maccel_set_growth_rate(float val); +void maccel_set_growth_rate(float val); +void maccel_set_offset(float val); +void maccel_set_limit(float val); + +void keyboard_post_init_maccel(void); diff --git a/users/bastardkb/features/maccel/maccel_via.c b/users/bastardkb/features/maccel/maccel_via.c new file mode 100644 index 00000000000..4a9a13b00d6 --- /dev/null +++ b/users/bastardkb/features/maccel/maccel_via.c @@ -0,0 +1,174 @@ +// Copyright 2024 burkfers (@burkfers) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "maccel.h" +#include "via.h" +#ifdef MACCEL_DEBUG +# include "debug.h" // IWYU pragma: keep +#endif + +_Static_assert(sizeof(maccel_config_t) == EECONFIG_USER_DATA_SIZE, "Mismatch in keyboard EECONFIG stored data"); + +enum via_maccel_channel { + // clang-format off + id_maccel = 24 + // clang-format on +}; +enum via_maccel_ids { + // clang-format off + id_maccel_takeoff = 1, + id_maccel_growth_rate = 2, + id_maccel_offset = 3, + id_maccel_limit = 4, + id_maccel_enabled = 5 + // clang-format on +}; + +#define MACCEL_VIA_TKO_MIN 0 +#define MACCEL_VIA_TKO_MAX 5 +#define MACCEL_VIA_GRO_MIN 0.01 +#define MACCEL_VIA_GRO_MAX 5 +#define MACCEL_VIA_OFS_MIN 0 +#define MACCEL_VIA_OFS_MAX 15 +#define MACCEL_VIA_LMT_MIN 0 +#define MACCEL_VIA_LMT_MAX 1 + +#define MACCEL_VIA_UINT16_MIN 0 +#define MACCEL_VIA_UINT16_MAX 60000 // Not using the full range for historic reasons. Should be changed with breaking change requiring via json update. + +#define PROJECT(val, rmin, rmax, tmin, tmax) (((float)(val - rmin) / (float)(rmax - rmin)) * (float)(tmax - tmin)) + tmin +#define PROJECT_TO_VIA(val, rmin, rmax) PROJECT(val, rmin, rmax, MACCEL_VIA_UINT16_MIN, MACCEL_VIA_UINT16_MAX) +#define PROJECT_FROM_VIA(val, tmin, tmax) PROJECT(val, MACCEL_VIA_UINT16_MIN, MACCEL_VIA_UINT16_MAX, tmin, tmax) + +#define COMBINE_UINT8(one, two) (two | (one << 8)) + +// Handle the data received by the keyboard from the VIA menus +void maccel_config_set_value(uint8_t *data) { + // data = [ value_id, value_data ] + uint8_t *value_id = &(data[0]); + uint8_t *value_data = &(data[1]); + + switch (*value_id) { + case id_maccel_takeoff: { + uint16_t takeoff = COMBINE_UINT8(value_data[0], value_data[1]); + + g_maccel_config.takeoff = PROJECT_FROM_VIA(takeoff, MACCEL_VIA_TKO_MIN, MACCEL_VIA_TKO_MAX); +#ifdef MACCEL_DEBUG + printf("MACCEL:via: TKO: %.3f grw: %.3f ofs: %.3f lmt: %.3f\n", g_maccel_config.takeoff, g_maccel_config.growth_rate, g_maccel_config.offset, g_maccel_config.limit); +#endif + break; + } + case id_maccel_growth_rate: { + uint16_t growth_rate = COMBINE_UINT8(value_data[0], value_data[1]); + + g_maccel_config.growth_rate = PROJECT_FROM_VIA(growth_rate, MACCEL_VIA_GRO_MIN, MACCEL_VIA_GRO_MAX); +#ifdef MACCEL_DEBUG + printf("MACCEL:via: tko: %.3f GRW: %.3f ofs: %.3f lmt: %.3f\n", g_maccel_config.takeoff, g_maccel_config.growth_rate, g_maccel_config.offset, g_maccel_config.limit); +#endif + break; + } + case id_maccel_offset: { + uint16_t offset = COMBINE_UINT8(value_data[0], value_data[1]); + + g_maccel_config.offset = PROJECT_FROM_VIA(offset, MACCEL_VIA_OFS_MIN, MACCEL_VIA_OFS_MAX); +#ifdef MACCEL_DEBUG + printf("MACCEL:via: tko: %.3f grw: %.3f OFS: %.3f lmt: %.3f\n", g_maccel_config.takeoff, g_maccel_config.growth_rate, g_maccel_config.offset, g_maccel_config.limit); +#endif + break; + } + case id_maccel_limit: { + uint16_t limit = COMBINE_UINT8(value_data[0], value_data[1]); + + g_maccel_config.limit = PROJECT_FROM_VIA(limit, MACCEL_VIA_LMT_MIN, MACCEL_VIA_LMT_MAX); +#ifdef MACCEL_DEBUG + printf("MACCEL:via: tko: %.3f grw: %.3f ofs: %.3f LMT: %.3f\n", g_maccel_config.takeoff, g_maccel_config.growth_rate, g_maccel_config.offset, g_maccel_config.limit); +#endif + break; + } + case id_maccel_enabled: { + g_maccel_config.enabled = value_data[0]; + break; + } + } +} + +// Handle the data sent by the keyboard to the VIA menus +void maccel_config_get_value(uint8_t *data) { + // data = [ value_id, value_data ] + uint8_t *value_id = &(data[0]); + uint8_t *value_data = &(data[1]); + + switch (*value_id) { + case id_maccel_takeoff: { + // uint16_t takeoff = (g_maccel_config.takeoff - 0.5) / (4.5 / 6) * 10000; + uint16_t takeoff = PROJECT_TO_VIA(g_maccel_config.takeoff, MACCEL_VIA_TKO_MIN, MACCEL_VIA_TKO_MAX); + value_data[0] = takeoff >> 8; + value_data[1] = takeoff & 0xFF; + break; + } + case id_maccel_growth_rate: { + uint16_t growth_rate = PROJECT_TO_VIA(g_maccel_config.growth_rate, MACCEL_VIA_GRO_MIN, MACCEL_VIA_GRO_MAX); + value_data[0] = growth_rate >> 8; + value_data[1] = growth_rate & 0xFF; + break; + } + case id_maccel_offset: { + uint16_t offset = PROJECT_TO_VIA(g_maccel_config.offset, MACCEL_VIA_OFS_MIN, MACCEL_VIA_OFS_MAX); + value_data[0] = offset >> 8; + value_data[1] = offset & 0xFF; + break; + } + case id_maccel_limit: { + uint16_t limit = PROJECT_TO_VIA(g_maccel_config.limit, MACCEL_VIA_LMT_MIN, MACCEL_VIA_LMT_MAX); + value_data[0] = limit >> 8; + value_data[1] = limit & 0xFF; + break; + } + case id_maccel_enabled: { + value_data[0] = g_maccel_config.enabled; + break; + } + } +} + +// Save the data to persistent memory after changes are made +void maccel_config_save(void) { + eeconfig_update_user_datablock(&g_maccel_config); +} + +void via_custom_value_command_kb(uint8_t *data, uint8_t length) { + uint8_t *command_id = &(data[0]); + uint8_t *channel_id = &(data[1]); + uint8_t *value_and_data = &(data[2]); + + if (*channel_id == id_maccel) { + switch (*command_id) { + case id_custom_set_value: + maccel_config_set_value(value_and_data); + break; + case id_custom_get_value: + maccel_config_get_value(value_and_data); + break; + case id_custom_save: + maccel_config_save(); + break; + default: + *command_id = id_unhandled; + break; + } + return; + } + + *command_id = id_unhandled; +} + +void eeconfig_init_user(void) { + // Write default value to EEPROM now + eeconfig_update_user_datablock(&g_maccel_config); +} + +// On Keyboard startup +void keyboard_post_init_maccel(void) { + // Read custom menu variables from memory + eeconfig_read_user_datablock(&g_maccel_config); +} diff --git a/users/bastardkb/features/maccel/readme.md b/users/bastardkb/features/maccel/readme.md new file mode 100644 index 00000000000..d8437b5eb8c --- /dev/null +++ b/users/bastardkb/features/maccel/readme.md @@ -0,0 +1,295 @@ +# Mouse acceleration + +This feature was born from the frustration of not having a tweakable acceleration curve that could work between OSes and hosts and be specific to one device, catalyzed by multiple users expressing interest. + +## Installation + +### Installation (keymap) + +Choose this route if you do not maintain a userspace. + +Place the `maccel` directory in your keymap directory. For example, if you were using the `via` keymap on a BastardKB Dilemma Max, you'd place it to be `keyboards/bastardkb/dilemma/4x6_4/keymaps/via/maccel/`. + +Add to `rules.mk` (Create it if it does not exist): +```make +# MACCEL +SRC += ./maccel/maccel.c +ifeq ($(strip $(VIA_ENABLE)), yes) + ifeq ($(strip $(MACCEL_VIA_ENABLE)), yes) + SRC += ./maccel/maccel_via.c + endif +endif +OPT_DEFS += -DMACCEL_ENABLE +``` + +Add to your `keymap.c`, near the top: +```c +#ifdef MACCEL_ENABLE + #include "maccel/maccel.h" +#endif +``` + +Continue with "Installation (common)", below. + +### Installation (userspace) + +Choose this route if you maintain a userspace. + +Place the `maccel` directory within `features/` in your userspace. For example, if your username was burkfers, you'd place it to be `users/burkfers/features/maccel/`. + +Add to `rules.mk` in the top level of your userspace (Create it if not present): + +```make +MACCEL_ENABLE = yes + +include $(USER_PATH)/features/maccel/rules.mk +``` + +You may instead place `MACCEL_ENABLE = yes` in your keymap's `rules.mk` if you wish to only enable maccel for some boards. + +Add to your userspace source file, near the top: +```c +#ifdef MACCEL_ENABLE + #include "features/maccel/maccel.h" +#endif +``` + +Continue with "Installation (common)", below. + +### Installation (common) + +Make sure the `pointing_device_task_user()` function in your `keymap.c` or userspace-sources exists, and contains the following code: +```c +report_mouse_t pointing_device_task_user(report_mouse_t mouse_report) { + // ... +#ifdef MACCEL_ENABLE + return pointing_device_task_maccel(mouse_report); +#endif +} +``` + +To minimize the chance of maxing out the mouse reports, it is recommended to define extended mouse reports in your config.h: +```c +#define MOUSE_EXTENDED_REPORT +``` + +See the section on runtime adjusting by keycodes and on via support for installation steps for these optional features. + +## Configuration +Before configuring maccel, make sure you have turned off your OS acceleration settings: On Windows, this setting is called "Enhance pointer precision". And make sure there isn't any 3rd party mouse acceleration software running. + +Several characteristics of the acceleration curve can be tweaked by adding relevant defines to `config.h`: +```c +#define MACCEL_TAKEOFF 2.0 // lower/higher value = curve takes off more smoothly/abruptly +#define MACCEL_GROWTH_RATE 0.25 // lower/higher value = curve reaches its upper limit slower/faster +#define MACCEL_OFFSET 2.2 // lower/higher value = acceleration kicks in earlier/later +#define MACCEL_LIMIT 0.2 // lower limit of accel curve (minimum acceleration factor) +``` +[![](assets/accel_curve.png)](https://www.desmos.com/calculator/k9vr0y2gev) + +The graph above shows the acceleration curve. You can interpret this graph as follows: the horizontal axis is input velocity (ie. how fast you are physically moving your mouse/trackball/trackpad); the vertical axis is the acceleration factor, which is the factor with which the input speed will be multiplied, resulting in your new output speed on screen. You can also understand this as a DPI scaling factor: the curve maxes out at 1, meaning your mouse sensitivity will never go higher than your default DPI setting; at the start of the curve your sensitivity is scaled down to a minimum that can be set by the LIMIT variable. The limit in this example is 0.2, which means at the lowest speeds your mouse sensitivity is scaled down to an equivalent of 0.2 times your default DPI. + +**If you click on the image of the curve**, you will be linked to Desmos, where you can play around with the variables to understand how each of them affect the shape of the curve. But in short: + +* The TAKEOFF variable controls how smoothly or abruptly the acceleration curve takes off. A higher value will make it take off more abruptly, a lower value smoothens out the start of the curve. + +* The GROWTH_RATE variable sets the growth rate of the acceleration curve. A lower value will result in a flatter curve which takes longer to reach its LIMIT. A higher value will result in a steeper curve, which will reach its LIMIT faster. + +* The OFFSET variable moves the entire curve towards the right. Offsetting the curve to the right means acceleration will kick in later, which is useful for low speed precision - in effect what you would otherwise have used SNIPING mode for. The maccel feature basically eliminates the need for a sniping mode. + +* The LIMIT variable sets the lower limit for the acceleration curve. This is the minimum acceleration factor at which the curve will start. In effect this adjusts the sensitivity for low speed precision movements. + +* The upper limit of the curve is fixed at 1, which means that at high speed sensitivity equals your default DPI. If you want to adjust high speed sensitivity, adjust your DPI. + +A good starting point for tweaking your settings, is to set your default DPI slightly higher than what you'd use without acceleration. Then set your LIMIT variable to a factor that would scale down to what you normally might have set your sniping DPI. For example, if your usual default DPI is 900, you might set it now to 1000. And if your usual sniping DPI is 200, you might set your LIMIT to 0.2 (0.2*1000=200). From there you can start playing around with the variables until you arrive at something to your liking. + +**Debug console**: To aid in dialing in your settings just right, a debug mode exists to print mathy details to the console. The debug console will print your current DPI setting and variable settings, as well as the acceleration factor, the input and output velocity, and the input and output distance. Refer to the QMK documentation on how to *enable the console and debugging*, then enable mouse acceleration debugging in `config.h`: +```c +#define MACCEL_DEBUG +/* + * Requires enabling float support for printf! + */ +#undef PRINTF_SUPPORT_DECIMAL_SPECIFIERS +#define PRINTF_SUPPORT_DECIMAL_SPECIFIERS 1 +``` + +## Runtime adjusting of curve parameters by keycodes (optional) + +### Additional required installation steps + +To use keycodes to adjust the parameters without recompiling, two more build steps are required. +First, add five keycodes to your keycode enum. You may choose different names, as long as you use the same names in the following step. If you are not yet using custom keycodes, add the following snippet to `keymap.c`: +```c +enum my_keycodes { + MA_TOGGLE = QK_USER, // toggle mouse acceleration + MA_TAKEOFF, // mouse acceleration curve takeoff (initial acceleration) step key + MA_GROWTH_RATE, // mouse acceleration curve growth rate step key + MA_OFFSET, // mouse acceleration curve offset step key + MA_LIMIT, // mouse acceleration curve limit step key +}; +``` +Next, add another shim, this time to `process_record_user`. If you have not previously implemented this function, simply place the following snippet in your `keymap.c`: +```c +bool process_record_user(uint16_t keycode, keyrecord_t *record) { + if (!process_record_maccel(keycode, record, MA_TOGGLE, MA_TAKEOFF, MA_GROWTH_RATE, MA_OFFSET, MA_LIMIT)) { + return false; + } + /* insert your own macros here */ + return true; +} +``` +Take care to use the same names that you used in the previous step. + +See the configuration section on how to enable this feature once you have set it up. + +Once the additional keycodes and shim are added, this feature can be enabled in `config.h`: +``` +#define MACCEL_USE_KEYCODES +``` + +Lastly, place the new keycodes on your keymap. + +--- +### Acceleration keycode usage + +The four keycodes can be used to adjust the curve parameters. This is *not* persisted unless you also enabled the via option - Adjusted values are printed to the console to aid in finding the right settings for `config.h`. +The step keys will adjust the parameters by the following amounts, which can optionally be adjusted: + +| Parameter | Default step value | Define name | +| --- | --- | --- | +| Takeoff | `+0.01` | `MACCEL_TAKEOFF_STEP` | +| Growth rate | `+0.01` | `MACCEL_GROWTH_RATE_STEP` | +| Offset | `+0.1` | `MACCEL_OFFSET_STEP` | +| Limit | `+0.1` | `MACCEL_LIMIT_STEP` | + +The modifier keys can be used to alter the step effect: + +| Modifier | Effect | +| --- | --- | +| Shift | Reverse step (subtract instead of adding) | +| Control | `step value * 10` (Step 10 times faster) | + +Modifiers can be combined. + +With every adjustment, an informational message is printed to the console. + +## VIA support + +![](assets/via.png) + +Mouse acceleration can now be configured though via. If your keyboard is not already supported by via, you must first [create a via definition](https://www.caniusevia.com/docs/specification). + +Add to `rules.mk`, *before* the include added previously: +```make +MACCEL_VIA_ENABLE = yes +`````` + +Add a shim to `keyboard_post_init_user`: +```c +void keyboard_post_init_user(void) { + keyboard_post_init_maccel(); +} +``` + +Add the entire function to your keymap if not already present, or insert the call to `keyboard_post_init_maccel` in your existing implementation. + +You must also configure the size of the EEPROM user block by placing the following define in `config.h`: +```c +#define EECONFIG_USER_DATA_SIZE 20 +``` + +Please be aware of the following caveats: +- The maccel via support takes over your eeprom user block. If you are already storing values in eeprom in your userspace, you must manually merge the features. +- The maccel via support implements `via_custom_value_command_kb`. This is not compatible with keyboards that already add custom features to via. If your keyboard has custom via configuration, you must manually shim the keyboard-level callback. + +Create a custom via definition: Find your keyboard's via definition in the [via keyboards repository](https://github.com/the-via/keyboards/tree/master/v3) if you did not create your own. + +Extend its `menus` configuration by placing the [menu definition](assets/via.json) on the `menu` node. Completed examples are provided for these keyboards: + +- [Charybdis 3x5](assets/charybdis_3x5.json) +- [Charybdis 3x6](assets/charybdis_3x6.json) +- [Charybdis 4x6](assets/charybdis_4x6.json) +- [Dilemma v2](assets/dilemma_v2.json) +- [Dilemma Max](assets/dilemma_max.json) + +Finally, after flashing the firmware to your board, load the custom via definition in the design tab in [via](https://usevia.app) (you may have to enable the "Show Design Tab" option in the settings). + +# Setup checklist + +- Place files in `users/YOUR_USERNAME/features/maccel/` +- Include `maccel/rules.mk` in your `rules.mk` +- Shim `pointing_device_task_user` +- Add configuration defines for parameters and optionally debugging +- Optional: Config keycodes: + - Enable keycode support by define + - Create five keycodes in the keycode enum + - Shim `process_record_user` +- Optional: VIA support: + - Enable in `rules.mk` + - Shim `keyboard_post_init_user` + - Set user eeprom data block size + - Create custom via json and side-load it in the web app + +## Limitations + +**Mac OS compatibility:** + +Unfortunately it seems maccel does not work well together with Mac OS. We cannot say with certainty why, but it seems like Apple does some post processing to the mouse reports that distorts the maccel result, even when the OS level acceleration is disabled. But some consolation for mac users: the default mac OS acceleration implementation seems to be quite good. + +**Sensor compatibility:** +* PMW3360: fully compatible, elaborately tested +* Other PMW33xx sensors will very likely perform equally well (but not tested so far) +* Cirque trackpad: compatible, limited testing +* Azoteq: still some issues to be resolved, not yet compatible +* No other QMK compatible sensors have been tested so far. We expect most sensors to work fine with maccel, but there could always be unexpected driver/firmware related conflicts we are not aware of. +* If you are using maccel successfully (or unsuccessfully) with a sensor that isn't listed here, we'd love to hear! + +**MCU compatibility:** +* This feature makes extensive use of floating point operations, and as such is not likely to work on AVR processors. So far tested only on RP2040! + +It is currently unknown how the un-throttled polling when used with `POINTING_DEVICE_MOTION_PIN` would interact with the expensive calculations. + +## Breaking changes + +### 2024 March 12 + +This new release changes the acceleration curve from a up-scaling curve to a down-scaling curve, to match how other acceleration tools work, and to avoid forcing users to set a very low DPI setting - this had been the goal from the start, but it took until now to overcome the technical challenges to make this work smoothly. + +See the configuration bit of this readme for an explanation of how the new curve works. This change means that you will have to readjust your variables; but do not worry, it is fairly easy to get this dialed in to *exactly* to how you had it set before: + +* First, change your default DPI: $DPI_{new} = DPI_{old} * {limit}_{old}$ +* Second, change your LIMIT variable (which is now lower instead of upper limit): $limit_{new} = \dfrac{DPI_{old}}{DPI_{new}}$ +* Your other variables can remain the same. +* If using via, make sure to clear EEPROM for the new settings to take effect. + +### 2024 March 10 + +A keycode for toggling mouse acceleration was added: If you enabled maccel keycodes, you must add a fifth keycode to your enum and add it to the shim between the record and takeoff arguments. Don't forget to place it on your keymap! + +### 2024 March 1 + +If you're updating from a previous version, you will have to make manual adjustments to your integration. Refer to the instructions for details on what the current version expects: +- The parameters have changed names and were expanded: + - `STEEPNESS` is now `GROWTH_RATE`, `TAKEOFF` was added + - Change the define names in your `config.h` for the parameter settings and step settings (if applicable) + - If using keycodes: Change the shim in `process_record_user` to call a fourth keycode +- If using via: Clear EEPROM and use a new via json. Do NOT load a previous via backup without adjusting the maccel values to the new format! + +If you set GROWTH_RATE to your previous value of `STEEPNESS` and keep `TAKEOFF` at a high value (eg. `10`), the behavior will be similar to previous versions. + +## Release history +- 2024 March 12 - Release of improved down scaling accel curve +- 2024 March 10 - Addition of toggle keycode +- 2024 March 1 - Release of new four-parameter acceleration curve +- 2024 February 23 - New four-parameter acceleration curve and improved documentation +- 2024 February 07 - Experimental new DPI correction to achieve consistent acceleration behavior across different user DPI settings. +- 2024 February 06 - First release candidate. Feedback welcome! + +## Credits +Thanks to everyone who helped! +Including, but not limited to: +- Wimads (@wimads) and burkfers (@burkfers) wrote most of the code +- ankostis (@ankostis) for catalyzing discussion about improving the acceleration curve and providing several enhancements +- Quentin (@balanstik) for insightful commentary on the math, and testing +- ouglop (@ouglop) for insightful commentary on the math +- Drashna Jael're (@drashna) for coding tips and their invaluable bag of magic C tricks diff --git a/users/bastardkb/features/maccel/rules.mk b/users/bastardkb/features/maccel/rules.mk new file mode 100644 index 00000000000..9ef6fc3f673 --- /dev/null +++ b/users/bastardkb/features/maccel/rules.mk @@ -0,0 +1,9 @@ +ifeq ($(strip $(MACCEL_ENABLE)), yes) + SRC += $(USER_PATH)/features/maccel/maccel.c + ifeq ($(strip $(VIA_ENABLE)), yes) + ifeq ($(strip $(MACCEL_VIA_ENABLE)), yes) + SRC += $(USER_PATH)/features/maccel/maccel_via.c + endif + endif + OPT_DEFS += -DMACCEL_ENABLE +endif diff --git a/users/bastardkb/rules.mk b/users/bastardkb/rules.mk new file mode 100644 index 00000000000..5a78670a70b --- /dev/null +++ b/users/bastardkb/rules.mk @@ -0,0 +1 @@ +include $(USER_PATH)/features/maccel/rules.mk