/* Copyright 2022 aki27 This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include QMK_KEYBOARD_H #include // Invert vertical scroll direction #ifndef COCOT_SCROLL_INV_DEFAULT # define COCOT_SCROLL_INV_DEFAULT 1 #endif // All the default values here refer to O-based array indexes // not actual values #ifndef COCOT_CPI_OPTIONS # define COCOT_CPI_OPTIONS { 250, 500, 750, 1000, 1250 } #endif #ifndef COCOT_CPI_DEFAULT # define COCOT_CPI_DEFAULT 4 #endif #ifndef COCOT_SCROLL_DIVIDERS # define COCOT_SCROLL_DIVIDERS { 1, 2, 3, 4, 5, 6 } #endif #ifndef COCOT_SCROLL_DIV_DEFAULT # define COCOT_SCROLL_DIV_DEFAULT 4 #endif #ifndef COCOT_ROTATION_ANGLE # define COCOT_ROTATION_ANGLE { -60, -45, -30, -15, 0, 15, 30, 45, 60 } #endif #ifndef COCOT_ROTATION_DEFAULT # define COCOT_ROTATION_DEFAULT 2 #endif cocot_config_t cocot_config; uint16_t cpi_array[] = COCOT_CPI_OPTIONS; uint16_t scrl_div_array[] = COCOT_SCROLL_DIVIDERS; int8_t angle_array[] = COCOT_ROTATION_ANGLE; #define CPI_OPTION_SIZE (sizeof(cpi_array) / sizeof(uint16_t)) #define SCRL_DIV_SIZE (sizeof(scrl_div_array) / sizeof(uint16_t)) #define ANGLE_SIZE (sizeof(angle_array) / sizeof(int8_t)) // Trackball State bool BurstState = false; // init burst state for Trackball module uint16_t MotionStart = 0; // Timer for accel, 0 is resting state // Scroll Accumulation static int16_t h_acm = 0; static int16_t v_acm = 0; void pointing_device_init_kb(void) { // set the CPI. pointing_device_set_cpi(cpi_array[cocot_config.cpi_idx]); adns5050_write_reg(0x22, 0b10000 | 0x80); } report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) { double rad = (double)angle_array[cocot_config.rotation_angle] * (M_PI / 180) * -1; int8_t x_rev = + mouse_report.x * cos(rad) - mouse_report.y * sin(rad); int8_t y_rev = + mouse_report.x * sin(rad) + mouse_report.y * cos(rad); if (cocot_get_scroll_mode()) { // rock scroll direction if (abs(x_rev) > abs(y_rev)) { y_rev = 0; } else { x_rev = 0; } // accumulate scroll h_acm += x_rev * cocot_config.scrl_inv; v_acm += y_rev * cocot_config.scrl_inv * -1; int8_t h_rev = h_acm >> scrl_div_array[cocot_config.scrl_div]; int8_t v_rev = v_acm >> scrl_div_array[cocot_config.scrl_div]; // clear accumulated scroll on assignment if (h_rev != 0) { if (mouse_report.h + h_rev > 127) { h_rev = 127 - mouse_report.h; } else if (mouse_report.h + h_rev < -127) { h_rev = -127 - mouse_report.h; } mouse_report.h += h_rev; h_acm -= h_rev << scrl_div_array[cocot_config.scrl_div]; } if (v_rev != 0) { if (mouse_report.v + v_rev > 127) { v_rev = 127 - mouse_report.v; } else if (mouse_report.v + v_rev < -127) { v_rev = -127 - mouse_report.v; } mouse_report.v += v_rev; v_acm -= v_rev << scrl_div_array[cocot_config.scrl_div]; } mouse_report.x = 0; mouse_report.y = 0; } else { mouse_report.x = x_rev; mouse_report.y = y_rev; } return pointing_device_task_user(mouse_report); } bool process_record_kb(uint16_t keycode, keyrecord_t* record) { // xprintf("KL: kc: %u, col: %u, row: %u, pressed: %u\n", keycode, record->event.key.col, record->event.key.row, record->event.pressed); if (!process_record_user(keycode, record)) { return false; } if (keycode == CPI_SW && record->event.pressed) { cocot_config.cpi_idx = (cocot_config.cpi_idx + 1) % CPI_OPTION_SIZE; eeconfig_update_kb(cocot_config.raw); pointing_device_set_cpi(cpi_array[cocot_config.cpi_idx]); } if (keycode == SCRL_SW && record->event.pressed) { cocot_config.scrl_div = (cocot_config.scrl_div + 1) % SCRL_DIV_SIZE; eeconfig_update_kb(cocot_config.raw); } if (keycode == ROT_R15 && record->event.pressed) { cocot_config.rotation_angle = (cocot_config.rotation_angle + 1) % ANGLE_SIZE; eeconfig_update_kb(cocot_config.raw); } if (keycode == ROT_L15 && record->event.pressed) { cocot_config.rotation_angle = (ANGLE_SIZE + cocot_config.rotation_angle - 1) % ANGLE_SIZE; eeconfig_update_kb(cocot_config.raw); } if (keycode == SCRL_IN && record->event.pressed) { cocot_config.scrl_inv = -cocot_config.scrl_inv; eeconfig_update_kb(cocot_config.raw); } if (keycode == SCRL_TO && record->event.pressed) { { cocot_config.scrl_mode ^= 1; } } if (keycode == SCRL_MO) { { cocot_config.scrl_mode ^= 1; } } return true; } void eeconfig_init_kb(void) { cocot_config.cpi_idx = COCOT_CPI_DEFAULT; cocot_config.scrl_div = COCOT_SCROLL_DIV_DEFAULT; cocot_config.rotation_angle = COCOT_ROTATION_DEFAULT; cocot_config.scrl_inv = COCOT_SCROLL_INV_DEFAULT; cocot_config.scrl_mode = false; eeconfig_update_kb(cocot_config.raw); eeconfig_init_user(); adns5050_write_reg(0x22, 0b10000 | 0x80); } void matrix_init_kb(void) { // is safe to just read CPI setting since matrix init // comes before pointing device init. cocot_config.raw = eeconfig_read_kb(); if (cocot_config.cpi_idx > CPI_OPTION_SIZE) // || cocot_config.scrl_div > SCRL_DIV_SIZE || cocot_config.rotation_angle > ANGLE_SIZE) { eeconfig_init_kb(); } matrix_init_user(); } bool cocot_get_scroll_mode(void) { return cocot_config.scrl_mode; } void cocot_set_scroll_mode(bool mode) { cocot_config.scrl_mode = mode; } // OLED utility #ifdef OLED_ENABLE bool oled_task_kb(void) { if (!oled_task_user()) { return false; } render_logo(); oled_write_layer_state(); return false; } oled_rotation_t oled_init_kb(oled_rotation_t rotation) { return OLED_ROTATION_0; } static const char PROGMEM cocot_logo[] = { 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0}; void render_logo(void) { oled_write_P(cocot_logo, false); }; void oled_write_layer_state(void) { oled_write_P(PSTR(" "), false); switch (get_highest_layer(layer_state | default_layer_state)) { case 0: oled_write_P(PSTR("Base "), false); break; case 1: oled_write_P(PSTR("Lower"), false); break; case 2: oled_write_P(PSTR("Raise"), false); break; case 3: oled_write_P(PSTR("Mouse"), false); break; case 4: oled_write_P(PSTR("L4 "), false); break; case 5: oled_write_P(PSTR("L5 "), false); break; case 6: oled_write_P(PSTR("L6 "), false); break; default: oled_write_P(PSTR("Undef"), false); break; } oled_write_P(PSTR("/"), false); if (cocot_get_scroll_mode()){ oled_write_P(PSTR("S"), false); } else{ oled_write_P(PSTR("C"), false); } char cpi[5]; char scroll_div[3]; char angle[4]; snprintf(cpi, 5, "%4d", cpi_array[cocot_config.cpi_idx]); snprintf(scroll_div, 3, "%2d", scrl_div_array[cocot_config.scrl_div]); snprintf(angle, 4, "%3d", angle_array[cocot_config.rotation_angle]); oled_write_P(PSTR("/"), false); oled_write(cpi, false); oled_write_P(PSTR("/"), false); oled_write(scroll_div, false); oled_write_P(PSTR("/"), false); oled_write(angle, false); } #endif