pimoroni_trackball.c (5792B)
1 /* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com> 2 * 3 * This program is free software: you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License as published by 5 * the Free Software Foundation, either version 2 of the License, or 6 * (at your option) any later version. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16 17 #include QMK_KEYBOARD_H 18 #include "pimoroni_trackball.h" 19 #include "i2c_master.h" 20 #include "action.h" 21 #include "timer.h" 22 #include "print.h" 23 24 static uint8_t scrolling = 0; 25 static int16_t x_offset = 0; 26 static int16_t y_offset = 0; 27 static int16_t h_offset = 0; 28 static int16_t v_offset = 0; 29 static float precisionSpeed = 1; 30 31 static uint16_t i2c_timeout_timer; 32 33 #ifndef I2C_TIMEOUT 34 # define I2C_TIMEOUT 100 35 #endif 36 #ifndef I2C_WAITCHECK 37 # define I2C_WAITCHECK 1000 38 #endif 39 #ifndef MOUSE_DEBOUNCE 40 # define MOUSE_DEBOUNCE 5 41 #endif 42 43 void trackball_set_rgbw(uint8_t red, uint8_t green, uint8_t blue, uint8_t white) { 44 uint8_t data[] = {0x00, red, green, blue, white}; 45 i2c_transmit(TRACKBALL_ADDRESS, data, sizeof(data), I2C_TIMEOUT); 46 } 47 48 int16_t mouse_offset(uint8_t positive, uint8_t negative, int16_t scale) { 49 int16_t offset = (int16_t)positive - (int16_t)negative; 50 int16_t magnitude = (int16_t)(scale * offset * offset * precisionSpeed); 51 return offset < 0 ? -magnitude : magnitude; 52 } 53 54 void update_member(int8_t* member, int16_t* offset) { 55 if (*offset > 127) { 56 *member = 127; 57 *offset -= 127; 58 } else if (*offset < -127) { 59 *member = -127; 60 *offset += 127; 61 } else { 62 *member = *offset; 63 *offset = 0; 64 } 65 } 66 67 __attribute__((weak)) void trackball_check_click(bool pressed, report_mouse_t* mouse) { 68 if (pressed) { 69 mouse->buttons |= MOUSE_BTN1; 70 } else { 71 mouse->buttons &= ~MOUSE_BTN1; 72 } 73 } 74 75 bool process_record_user(uint16_t keycode, keyrecord_t* record) { 76 /* If Mousekeys is disabled, then use handle the mouse button 77 * keycodes. This makes things simpler, and allows usage of 78 * the keycodes in a consistent manner. But only do this if 79 * Mousekeys is not enable, so it's not handled twice. 80 */ 81 #ifndef MOUSEKEY_ENABLE 82 if (IS_MOUSEKEY_BUTTON(keycode)) { 83 report_mouse_t currentReport = pointing_device_get_report(); 84 if (record->event.pressed) { 85 currentReport.buttons |= 1 << (keycode - QK_MOUSE_BUTTON_1); 86 } else { 87 currentReport.buttons &= ~(1 << (keycode - QK_MOUSE_BUTTON_1)); 88 } 89 pointing_device_set_report(currentReport); 90 pointing_device_send(); 91 return false; 92 } 93 #endif 94 95 return true; 96 } 97 98 void trackball_register_button(bool pressed, enum mouse_buttons button) { 99 report_mouse_t currentReport = pointing_device_get_report(); 100 if (pressed) { 101 currentReport.buttons |= button; 102 } else { 103 currentReport.buttons &= ~button; 104 } 105 pointing_device_set_report(currentReport); 106 } 107 108 float trackball_get_precision(void) { return precisionSpeed; } 109 void trackball_set_precision(float precision) { precisionSpeed = precision; } 110 bool trackball_is_scrolling(void) { return scrolling; } 111 void trackball_set_scrolling(bool scroll) { scrolling = scroll; } 112 113 114 __attribute__((weak)) void pointing_device_init(void) { trackball_set_rgbw(0x80, 0x00, 0x00, 0x00); } 115 116 bool pointing_device_task(void) { 117 static bool debounce; 118 static uint16_t debounce_timer; 119 uint8_t state[5] = {}; 120 if (timer_elapsed(i2c_timeout_timer) > I2C_WAITCHECK) { 121 if (i2c_read_register(TRACKBALL_ADDRESS, 0x04, state, 5, I2C_TIMEOUT) == I2C_STATUS_SUCCESS) { 122 if (!state[4] && !debounce) { 123 if (scrolling) { 124 #ifdef PIMORONI_TRACKBALL_INVERT_X 125 h_offset += mouse_offset(state[2], state[3], 1); 126 #else 127 h_offset -= mouse_offset(state[2], state[3], 1); 128 #endif 129 #ifdef PIMORONI_TRACKBALL_INVERT_Y 130 v_offset += mouse_offset(state[1], state[0], 1); 131 #else 132 v_offset -= mouse_offset(state[1], state[0], 1); 133 #endif 134 } else { 135 #ifdef PIMORONI_TRACKBALL_INVERT_X 136 x_offset -= mouse_offset(state[2], state[3], 5); 137 #else 138 x_offset += mouse_offset(state[2], state[3], 5); 139 #endif 140 #ifdef PIMORONI_TRACKBALL_INVERT_Y 141 y_offset -= mouse_offset(state[1], state[0], 5); 142 #else 143 y_offset += mouse_offset(state[1], state[0], 5); 144 #endif 145 } 146 } else { 147 if (state[4]) { 148 debounce = true; 149 debounce_timer = timer_read(); 150 } 151 } 152 } else { 153 i2c_timeout_timer = timer_read(); 154 } 155 } 156 157 if (timer_elapsed(debounce_timer) > MOUSE_DEBOUNCE) debounce = false; 158 159 report_mouse_t mouse = pointing_device_get_report(); 160 // trackball_check_click(state[4] & (1 << 7), &mouse); 161 162 #ifndef PIMORONI_TRACKBALL_ROTATE 163 update_member(&mouse.x, &x_offset); 164 update_member(&mouse.y, &y_offset); 165 update_member(&mouse.h, &h_offset); 166 update_member(&mouse.v, &v_offset); 167 #else 168 update_member(&mouse.x, &y_offset); 169 update_member(&mouse.y, &x_offset); 170 update_member(&mouse.h, &v_offset); 171 update_member(&mouse.v, &h_offset); 172 #endif 173 pointing_device_set_report(mouse); 174 return pointing_device_send(); 175 }