tractyl_manuform.c (15727B)
1 /* Copyright 2020 Christopher Courtney <drashna@live.com> (@drashna) 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 "tractyl_manuform.h" 18 #ifdef POINTING_DEVICE_ENABLE 19 # include "pointing_device.h" 20 #endif 21 #include "transactions.h" 22 #include <string.h> 23 24 #ifdef CONSOLE_ENABLE 25 # include "print.h" 26 #endif // CONSOLE_ENABLE 27 28 #ifdef POINTING_DEVICE_ENABLE 29 # ifndef CHARYBDIS_MINIMUM_DEFAULT_DPI 30 # define CHARYBDIS_MINIMUM_DEFAULT_DPI 400 31 # endif // CHARYBDIS_MINIMUM_DEFAULT_DPI 32 33 # ifndef CHARYBDIS_DEFAULT_DPI_CONFIG_STEP 34 # define CHARYBDIS_DEFAULT_DPI_CONFIG_STEP 200 35 # endif // CHARYBDIS_DEFAULT_DPI_CONFIG_STEP 36 37 # ifndef CHARYBDIS_MINIMUM_SNIPING_DPI 38 # define CHARYBDIS_MINIMUM_SNIPING_DPI 200 39 # endif // CHARYBDIS_MINIMUM_SNIPER_MODE_DPI 40 41 # ifndef CHARYBDIS_SNIPING_DPI_CONFIG_STEP 42 # define CHARYBDIS_SNIPING_DPI_CONFIG_STEP 100 43 # endif // CHARYBDIS_SNIPING_DPI_CONFIG_STEP 44 45 // Fixed DPI for drag-scroll. 46 # ifndef CHARYBDIS_DRAGSCROLL_DPI 47 # define CHARYBDIS_DRAGSCROLL_DPI 100 48 # endif // CHARYBDIS_DRAGSCROLL_DPI 49 50 # ifndef CHARYBDIS_DRAGSCROLL_BUFFER_SIZE 51 # define CHARYBDIS_DRAGSCROLL_BUFFER_SIZE 6 52 # endif // !CHARYBDIS_DRAGSCROLL_BUFFER_SIZE 53 54 typedef union { 55 uint8_t raw; 56 struct { 57 uint8_t pointer_default_dpi : 4; // 16 steps available. 58 uint8_t pointer_sniping_dpi : 2; // 4 steps available. 59 bool is_dragscroll_enabled : 1; 60 bool is_sniping_enabled : 1; 61 } __attribute__((packed)); 62 } charybdis_config_t; 63 64 static charybdis_config_t g_charybdis_config = {0}; 65 66 /** 67 * \brief Set the value of `config` from EEPROM. 68 * 69 * Note that `is_dragscroll_enabled` and `is_sniping_enabled` are purposefully 70 * ignored since we do not want to persist this state to memory. In practice, 71 * this state is always written to maximize write-performances. Therefore, we 72 * explicitly set them to `false` in this function. 73 */ 74 static void read_charybdis_config_from_eeprom(charybdis_config_t* config) { 75 config->raw = eeconfig_read_kb() & 0xff; 76 config->is_dragscroll_enabled = false; 77 config->is_sniping_enabled = false; 78 } 79 80 /** 81 * \brief Save the value of `config` to eeprom. 82 * 83 * Note that all values are written verbatim, including whether drag-scroll 84 * and/or sniper mode are enabled. `read_charybdis_config_from_eeprom(…)` 85 * resets these 2 values to `false` since it does not make sense to persist 86 * these across reboots of the board. 87 */ 88 static void write_charybdis_config_to_eeprom(charybdis_config_t* config) { 89 eeconfig_update_kb(config->raw); 90 } 91 92 /** \brief Return the current value of the pointer's default DPI. */ 93 static uint16_t get_pointer_default_dpi(charybdis_config_t* config) { 94 return (uint16_t)config->pointer_default_dpi * CHARYBDIS_DEFAULT_DPI_CONFIG_STEP + CHARYBDIS_MINIMUM_DEFAULT_DPI; 95 } 96 97 /** \brief Return the current value of the pointer's sniper-mode DPI. */ 98 static uint16_t get_pointer_sniping_dpi(charybdis_config_t* config) { 99 return (uint16_t)config->pointer_sniping_dpi * CHARYBDIS_SNIPING_DPI_CONFIG_STEP + CHARYBDIS_MINIMUM_SNIPING_DPI; 100 } 101 102 /** \brief Set the appropriate DPI for the input config. */ 103 static void maybe_update_pointing_device_cpi(charybdis_config_t* config) { 104 if (config->is_dragscroll_enabled) { 105 pointing_device_set_cpi(CHARYBDIS_DRAGSCROLL_DPI); 106 } else if (config->is_sniping_enabled) { 107 pointing_device_set_cpi(get_pointer_sniping_dpi(config)); 108 } else { 109 pointing_device_set_cpi(get_pointer_default_dpi(config)); 110 } 111 } 112 113 /** 114 * \brief Update the pointer's default DPI to the next or previous step. 115 * 116 * Increases the DPI value if `forward` is `true`, decreases it otherwise. 117 * The increment/decrement steps are equal to CHARYBDIS_DEFAULT_DPI_CONFIG_STEP. 118 */ 119 static void step_pointer_default_dpi(charybdis_config_t* config, bool forward) { 120 config->pointer_default_dpi += forward ? 1 : -1; 121 maybe_update_pointing_device_cpi(config); 122 } 123 124 /** 125 * \brief Update the pointer's sniper-mode DPI to the next or previous step. 126 * 127 * Increases the DPI value if `forward` is `true`, decreases it otherwise. 128 * The increment/decrement steps are equal to CHARYBDIS_SNIPING_DPI_CONFIG_STEP. 129 */ 130 static void step_pointer_sniping_dpi(charybdis_config_t* config, bool forward) { 131 config->pointer_sniping_dpi += forward ? 1 : -1; 132 maybe_update_pointing_device_cpi(config); 133 } 134 135 uint16_t charybdis_get_pointer_default_dpi(void) { 136 return get_pointer_default_dpi(&g_charybdis_config); 137 } 138 139 uint16_t charybdis_get_pointer_sniping_dpi(void) { 140 return get_pointer_sniping_dpi(&g_charybdis_config); 141 } 142 143 void charybdis_cycle_pointer_default_dpi_noeeprom(bool forward) { 144 step_pointer_default_dpi(&g_charybdis_config, forward); 145 } 146 147 void charybdis_cycle_pointer_default_dpi(bool forward) { 148 step_pointer_default_dpi(&g_charybdis_config, forward); 149 write_charybdis_config_to_eeprom(&g_charybdis_config); 150 } 151 152 void charybdis_cycle_pointer_sniping_dpi_noeeprom(bool forward) { 153 step_pointer_sniping_dpi(&g_charybdis_config, forward); 154 } 155 156 void charybdis_cycle_pointer_sniping_dpi(bool forward) { 157 step_pointer_sniping_dpi(&g_charybdis_config, forward); 158 write_charybdis_config_to_eeprom(&g_charybdis_config); 159 } 160 161 bool charybdis_get_pointer_sniping_enabled(void) { 162 return g_charybdis_config.is_sniping_enabled; 163 } 164 165 void charybdis_set_pointer_sniping_enabled(bool enable) { 166 g_charybdis_config.is_sniping_enabled = enable; 167 maybe_update_pointing_device_cpi(&g_charybdis_config); 168 } 169 170 bool charybdis_get_pointer_dragscroll_enabled(void) { 171 return g_charybdis_config.is_dragscroll_enabled; 172 } 173 174 void charybdis_set_pointer_dragscroll_enabled(bool enable) { 175 g_charybdis_config.is_dragscroll_enabled = enable; 176 maybe_update_pointing_device_cpi(&g_charybdis_config); 177 } 178 179 /** 180 * \brief Augment the pointing device behavior. 181 * 182 * Implement drag-scroll. 183 */ 184 static void pointing_device_task_charybdis(report_mouse_t* mouse_report) { 185 static int16_t scroll_buffer_x = 0; 186 static int16_t scroll_buffer_y = 0; 187 if (g_charybdis_config.is_dragscroll_enabled) { 188 # ifdef CHARYBDIS_DRAGSCROLL_REVERSE_X 189 scroll_buffer_x -= mouse_report->x; 190 # else 191 scroll_buffer_x += mouse_report->x; 192 # endif // CHARYBDIS_DRAGSCROLL_REVERSE_X 193 # ifdef CHARYBDIS_DRAGSCROLL_REVERSE_Y 194 scroll_buffer_y -= mouse_report->y; 195 # else 196 scroll_buffer_y += mouse_report->y; 197 # endif // CHARYBDIS_DRAGSCROLL_REVERSE_Y 198 mouse_report->x = 0; 199 mouse_report->y = 0; 200 if (abs(scroll_buffer_x) > CHARYBDIS_DRAGSCROLL_BUFFER_SIZE) { 201 mouse_report->h = scroll_buffer_x > 0 ? 1 : -1; 202 scroll_buffer_x = 0; 203 } 204 if (abs(scroll_buffer_y) > CHARYBDIS_DRAGSCROLL_BUFFER_SIZE) { 205 mouse_report->v = scroll_buffer_y > 0 ? 1 : -1; 206 scroll_buffer_y = 0; 207 } 208 } 209 } 210 211 report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) { 212 if (is_keyboard_master()) { 213 pointing_device_task_charybdis(&mouse_report); 214 mouse_report = pointing_device_task_user(mouse_report); 215 } 216 return mouse_report; 217 } 218 219 # if defined(POINTING_DEVICE_ENABLE) && !defined(NO_CHARYBDIS_KEYCODES) 220 /** \brief Whether SHIFT mod is enabled. */ 221 static bool has_shift_mod(void) { 222 # ifdef NO_ACTION_ONESHOT 223 return mod_config(get_mods()) & MOD_MASK_SHIFT; 224 # else 225 return mod_config(get_mods() | get_oneshot_mods()) & MOD_MASK_SHIFT; 226 # endif // NO_ACTION_ONESHOT 227 } 228 # endif // POINTING_DEVICE_ENABLE && !NO_CHARYBDIS_KEYCODES 229 230 /** 231 * \brief Outputs the Charybdis configuration to console. 232 * 233 * Prints the in-memory configuration structure to console, for debugging. 234 * Includes: 235 * - raw value 236 * - drag-scroll: on/off 237 * - sniping: on/off 238 * - default DPI: internal table index/actual DPI 239 * - sniping DPI: internal table index/actual DPI 240 */ 241 __attribute__((unused)) static void debug_charybdis_config_to_console(charybdis_config_t* config) { 242 # ifdef CONSOLE_ENABLE 243 dprintf("(charybdis) process_record_kb: config = {\n" 244 "\traw = 0x%X,\n" 245 "\t{\n" 246 "\t\tis_dragscroll_enabled=%u\n" 247 "\t\tis_sniping_enabled=%u\n" 248 "\t\tdefault_dpi=0x%X (%u)\n" 249 "\t\tsniping_dpi=0x%X (%u)\n" 250 "\t}\n" 251 "}\n", 252 config->raw, config->is_dragscroll_enabled, config->is_sniping_enabled, config->pointer_default_dpi, get_pointer_default_dpi(config), config->pointer_sniping_dpi, get_pointer_sniping_dpi(config)); 253 # endif // CONSOLE_ENABLE 254 } 255 256 bool process_record_kb(uint16_t keycode, keyrecord_t* record) { 257 if (!process_record_user(keycode, record)) { 258 return false; 259 } 260 # ifndef NO_CHARYBDIS_KEYCODES 261 switch (keycode) { 262 case POINTER_DEFAULT_DPI_FORWARD: 263 if (record->event.pressed) { 264 // Step backward if shifted, forward otherwise. 265 charybdis_cycle_pointer_default_dpi(/* forward= */ !has_shift_mod()); 266 } 267 break; 268 case POINTER_DEFAULT_DPI_REVERSE: 269 if (record->event.pressed) { 270 // Step forward if shifted, backward otherwise. 271 charybdis_cycle_pointer_default_dpi(/* forward= */ has_shift_mod()); 272 } 273 break; 274 case POINTER_SNIPING_DPI_FORWARD: 275 if (record->event.pressed) { 276 // Step backward if shifted, forward otherwise. 277 charybdis_cycle_pointer_sniping_dpi(/* forward= */ !has_shift_mod()); 278 } 279 break; 280 case POINTER_SNIPING_DPI_REVERSE: 281 if (record->event.pressed) { 282 // Step forward if shifted, backward otherwise. 283 charybdis_cycle_pointer_sniping_dpi(/* forward= */ has_shift_mod()); 284 } 285 break; 286 case SNIPING_MODE: 287 charybdis_set_pointer_sniping_enabled(record->event.pressed); 288 break; 289 case SNIPING_MODE_TOGGLE: 290 if (record->event.pressed) { 291 charybdis_set_pointer_sniping_enabled(!charybdis_get_pointer_sniping_enabled()); 292 } 293 break; 294 case DRAGSCROLL_MODE: 295 charybdis_set_pointer_dragscroll_enabled(record->event.pressed); 296 break; 297 case DRAGSCROLL_MODE_TOGGLE: 298 if (record->event.pressed) { 299 charybdis_set_pointer_dragscroll_enabled(!charybdis_get_pointer_dragscroll_enabled()); 300 } 301 break; 302 } 303 # endif // !NO_CHARYBDIS_KEYCODES 304 return true; 305 } 306 307 void eeconfig_init_kb(void) { 308 g_charybdis_config.raw = 0; 309 write_charybdis_config_to_eeprom(&g_charybdis_config); 310 maybe_update_pointing_device_cpi(&g_charybdis_config); 311 eeconfig_init_user(); 312 } 313 314 void matrix_power_up(void) { 315 pointing_device_task(); 316 } 317 318 void charybdis_config_sync_handler(uint8_t initiator2target_buffer_size, const void* initiator2target_buffer, uint8_t target2initiator_buffer_size, void* target2initiator_buffer) { 319 if (initiator2target_buffer_size == sizeof(g_charybdis_config)) { 320 memcpy(&g_charybdis_config, initiator2target_buffer, sizeof(g_charybdis_config)); 321 } 322 } 323 #endif // POINTING_DEVICE_ENABLE 324 325 __attribute__((weak)) void user_button_init(void) { 326 #ifdef USER_BUTTON_PIN 327 gpio_set_pin_input_high(USER_BUTTON_PIN); 328 #endif // USER_BUTTON_PIN 329 } 330 331 __attribute__((weak)) bool check_user_button_state(void) { 332 #ifdef USER_BUTTON_PIN 333 return !gpio_read_pin(USER_BUTTON_PIN); 334 #endif // USER_BUTTON_PIN 335 return false; 336 } 337 338 void keyboard_post_init_kb(void) { 339 #ifdef DEBUG_LED_PIN 340 gpio_set_pin_output(DEBUG_LED_PIN); 341 gpio_write_pin_low(DEBUG_LED_PIN); 342 #endif // DEBUG_LED_PIN 343 344 #ifdef POINTING_DEVICE_ENABLE 345 maybe_update_pointing_device_cpi(&g_charybdis_config); 346 transaction_register_rpc(RPC_ID_KB_CONFIG_SYNC, charybdis_config_sync_handler); 347 #endif // POINTING_DEVICE_ENABLE 348 keyboard_post_init_user(); 349 } 350 351 void keyboard_pre_init_kb(void) { 352 user_button_init(); 353 #ifdef POINTING_DEVICE_ENABLE 354 read_charybdis_config_from_eeprom(&g_charybdis_config); 355 #endif // POINTING_DEVICE_ENAcBLE 356 keyboard_pre_init_user(); 357 } 358 359 __attribute__((weak)) void execute_user_button_action(bool state) { 360 if (state) { 361 if (is_keyboard_master()) { 362 reset_keyboard(); 363 } else { 364 soft_reset_keyboard(); 365 } 366 } 367 } 368 369 void housekeeping_task_kb(void) { 370 static bool last_state = false; 371 bool state = check_user_button_state(); 372 if (state != last_state) { 373 last_state = state; 374 execute_user_button_action(state); 375 } 376 377 #ifdef POINTING_DEVICE_ENABLE 378 if (is_keyboard_master()) { 379 // Keep track of the last state, so that we can tell if we need to propagate to slave 380 static charybdis_config_t last_charybdis_config = {0}; 381 static uint32_t last_sync = 0; 382 bool needs_sync = false; 383 384 // Check if the state values are different 385 if (memcmp(&g_charybdis_config, &last_charybdis_config, sizeof(g_charybdis_config))) { 386 needs_sync = true; 387 memcpy(&last_charybdis_config, &g_charybdis_config, sizeof(g_charybdis_config)); 388 } 389 // Send to slave every 500ms regardless of state change 390 if (timer_elapsed32(last_sync) > 500) { 391 needs_sync = true; 392 } 393 394 // Perform the sync if requested 395 if (needs_sync) { 396 if (transaction_rpc_send(RPC_ID_KB_CONFIG_SYNC, sizeof(g_charybdis_config), &g_charybdis_config)) { 397 last_sync = timer_read32(); 398 } 399 } 400 } 401 #endif // POINTING_DEVICE_ENABLE 402 // no need for user function, is called already 403 } 404 405 #ifdef USER_BUTTON_PIN 406 /** 407 * @brief Replace and add upon the default bootmagic reset function. 408 * In this case, we also check the user button. 409 * 410 * @return true if the user button is pressed, or normal bootmagic key position. 411 * @return false if the user button is not pressed and normal bootmagic key position is not pressed. 412 */ 413 __attribute__((weak)) bool bootmagic_should_reset(void) { 414 uint8_t row = BOOTMAGIC_ROW; 415 uint8_t col = BOOTMAGIC_COLUMN; 416 417 # if defined(SPLIT_KEYBOARD) && defined(BOOTMAGIC_ROW_RIGHT) && defined(BOOTMAGIC_COLUMN_RIGHT) 418 if (!is_keyboard_left()) { 419 row = BOOTMAGIC_ROW_RIGHT; 420 col = BOOTMAGIC_COLUMN_RIGHT; 421 } 422 # endif 423 424 return matrix_get_row(row) & (1 << col) || check_user_button_state(); 425 } 426 #endif // USER_BUTTON_PIN 427 428 bool shutdown_kb(bool jump_to_bootloader) { 429 if (!shutdown_user(jump_to_bootloader)) { 430 return false; 431 } 432 #ifdef RGB_MATRIX_ENABLE 433 void rgb_matrix_update_pwm_buffers(void); 434 rgb_matrix_set_color_all(RGB_RED); 435 rgb_matrix_update_pwm_buffers(); 436 #endif // RGB_MATRIX_ENABLE 437 return true; 438 } 439 440 #ifdef POINTING_DEVICE_AUTO_MOUSE_ENABLE 441 bool is_mouse_record_kb(uint16_t keycode, keyrecord_t* record) { 442 if (IS_KB_KEYCODE(keycode)) { 443 return true; 444 } 445 return is_mouse_record_user(keycode, record); 446 } 447 #endif // POINTING_DEVICE_AUTO_MOUSE_ENABLE