qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

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