/* * Copyright (c) 2020 The ZMK Contributors * * SPDX-License-Identifier: MIT */ #include LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include #include #include #include // CLAMP #if CONFIG_MINIMAL_LIBC static float powf(float base, float exponent) { // poor man's power implementation rounds the exponent down to the nearest integer. float power = 1.0f; for (; exponent >= 1.0f; exponent--) { power = power * base; } return power; } #else #include #endif struct vector2d move_remainder = {0}; struct vector2d scroll_remainder = {0}; static int64_t ms_since_start(int64_t start, int64_t now, int64_t delay) { int64_t move_duration = now - (start + delay); // start can be in the future if there's a delay if (move_duration < 0) { move_duration = 0; } return move_duration; } static float speed(const struct mouse_config *config, float max_speed, int64_t duration_ms) { // Calculate the speed based on MouseKeysAccel // See https://en.wikipedia.org/wiki/Mouse_keys if (duration_ms > config->time_to_max_speed_ms || config->time_to_max_speed_ms == 0 || config->acceleration_exponent == 0) { return max_speed; } float time_fraction = (float)duration_ms / config->time_to_max_speed_ms; return max_speed * powf(time_fraction, config->acceleration_exponent); } static void track_remainder(float *move, float *remainder) { float new_move = *move + *remainder; *remainder = new_move - (int)new_move; *move = (int)new_move; } static struct vector2d update_movement(struct vector2d *remainder, const struct mouse_config *config, struct vector2d max_speed, int64_t now, int64_t *start_time) { struct vector2d move = {0}; if (max_speed.x == 0 && max_speed.y == 0) { *remainder = (struct vector2d){0}; return move; } int64_t move_duration = ms_since_start(*start_time, now, config->delay_ms); move = (struct vector2d){ .x = speed(config, max_speed.x, move_duration) * CONFIG_ZMK_MOUSE_TICK_DURATION / 1000, .y = speed(config, max_speed.y, move_duration) * CONFIG_ZMK_MOUSE_TICK_DURATION / 1000, }; track_remainder(&(move.x), &(remainder->x)); track_remainder(&(move.y), &(remainder->y)); return move; } static void mouse_tick_handler(const struct zmk_mouse_tick *tick) { struct vector2d move = update_movement(&move_remainder, &(tick->move_config), tick->max_move, tick->timestamp, tick->start_time); zmk_hid_mouse_movement_update((int16_t)CLAMP(move.x, INT16_MIN, INT16_MAX), (int16_t)CLAMP(move.y, INT16_MIN, INT16_MAX)); struct vector2d scroll = update_movement(&scroll_remainder, &(tick->scroll_config), tick->max_scroll, tick->timestamp, tick->start_time); zmk_hid_mouse_scroll_update((int8_t)CLAMP(scroll.x, INT8_MIN, INT8_MAX), (int8_t)CLAMP(scroll.y, INT8_MIN, INT8_MAX)); } int zmk_mouse_tick_listener(const zmk_event_t *eh) { const struct zmk_mouse_tick *tick = as_zmk_mouse_tick(eh); if (tick) { mouse_tick_handler(tick); return 0; } return 0; } ZMK_LISTENER(zmk_mouse_tick_listener, zmk_mouse_tick_listener); ZMK_SUBSCRIPTION(zmk_mouse_tick_listener, zmk_mouse_tick);