qmk_firmware

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

charybdis.c (13952B)


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