qmk_firmware

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

dilemma.c (12256B)


      1 /**
      2  * Copyright 2020 Christopher Courtney <drashna@live.com> (@drashna)
      3  * Copyright 2021 Quentin LEBASTARD <qlebastard@gmail.com>
      4  * Copyright 2022 Charly Delay <charly@codesink.dev> (@0xcharly)
      5  *
      6  * This program is free software: you can redistribute it and/or modify
      7  * it under the terms of the GNU General Publicw License as published by
      8  * the Free Software Foundation, either version 2 of the License, or
      9  * (at your option) any later version.
     10  *
     11  * This program is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14  * GNU General Public License for more details.
     15  *
     16  * You should have received a copy of the GNU General Public License
     17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     18  */
     19 
     20 #include "dilemma.h"
     21 
     22 #ifdef CONSOLE_ENABLE
     23 #    include "print.h"
     24 #endif // CONSOLE_ENABLE
     25 
     26 #ifdef POINTING_DEVICE_ENABLE
     27 #    ifndef DILEMMA_MINIMUM_DEFAULT_DPI
     28 #        define DILEMMA_MINIMUM_DEFAULT_DPI 400
     29 #    endif // DILEMMA_MINIMUM_DEFAULT_DPI
     30 
     31 #    ifndef DILEMMA_DEFAULT_DPI_CONFIG_STEP
     32 #        define DILEMMA_DEFAULT_DPI_CONFIG_STEP 200
     33 #    endif // DILEMMA_DEFAULT_DPI_CONFIG_STEP
     34 
     35 #    ifndef DILEMMA_MINIMUM_SNIPING_DPI
     36 #        define DILEMMA_MINIMUM_SNIPING_DPI 200
     37 #    endif // DILEMMA_MINIMUM_SNIPING_DPI
     38 
     39 #    ifndef DILEMMA_SNIPING_DPI_CONFIG_STEP
     40 #        define DILEMMA_SNIPING_DPI_CONFIG_STEP 100
     41 #    endif // DILEMMA_SNIPING_DPI_CONFIG_STEP
     42 
     43 // Fixed DPI for drag-scroll.
     44 #    ifndef DILEMMA_DRAGSCROLL_DPI
     45 #        define DILEMMA_DRAGSCROLL_DPI 100
     46 #    endif // DILEMMA_DRAGSCROLL_DPI
     47 
     48 #    ifndef DILEMMA_DRAGSCROLL_BUFFER_SIZE
     49 #        define DILEMMA_DRAGSCROLL_BUFFER_SIZE 6
     50 #    endif // !DILEMMA_DRAGSCROLL_BUFFER_SIZE
     51 
     52 typedef union {
     53     uint8_t raw;
     54     struct {
     55         uint8_t pointer_default_dpi : 4; // 16 steps available.
     56         uint8_t pointer_sniping_dpi : 2; // 4 steps available.
     57         bool    is_dragscroll_enabled : 1;
     58         bool    is_sniping_enabled : 1;
     59     } __attribute__((packed));
     60 } dilemma_config_t;
     61 
     62 static dilemma_config_t g_dilemma_config = {0};
     63 
     64 /**
     65  * \brief Set the value of `config` from EEPROM.
     66  *
     67  * Note that `is_dragscroll_enabled` and `is_sniping_enabled` are purposefully
     68  * ignored since we do not want to persist this state to memory.  In practice,
     69  * this state is always written to maximize write-performances.  Therefore, we
     70  * explicitly set them to `false` in this function.
     71  */
     72 static void read_dilemma_config_from_eeprom(dilemma_config_t* config) {
     73     config->raw                   = eeconfig_read_kb() & 0xff;
     74     config->is_dragscroll_enabled = false;
     75     config->is_sniping_enabled    = false;
     76 }
     77 
     78 /**
     79  * \brief Save the value of `config` to eeprom.
     80  *
     81  * Note that all values are written verbatim, including whether drag-scroll
     82  * and/or sniper mode are enabled.  `read_dilemma_config_from_eeprom(…)`
     83  * resets these 2 values to `false` since it does not make sense to persist
     84  * these across reboots of the board.
     85  */
     86 static void write_dilemma_config_to_eeprom(dilemma_config_t* config) {
     87     eeconfig_update_kb(config->raw);
     88 }
     89 
     90 /** \brief Return the current value of the pointer's default DPI. */
     91 static uint16_t get_pointer_default_dpi(dilemma_config_t* config) {
     92     return (uint16_t)config->pointer_default_dpi * DILEMMA_DEFAULT_DPI_CONFIG_STEP + DILEMMA_MINIMUM_DEFAULT_DPI;
     93 }
     94 
     95 /** \brief Return the current value of the pointer's sniper-mode DPI. */
     96 static uint16_t get_pointer_sniping_dpi(dilemma_config_t* config) {
     97     return (uint16_t)config->pointer_sniping_dpi * DILEMMA_SNIPING_DPI_CONFIG_STEP + DILEMMA_MINIMUM_SNIPING_DPI;
     98 }
     99 
    100 /** \brief Set the appropriate DPI for the input config. */
    101 static void maybe_update_pointing_device_cpi(dilemma_config_t* config) {
    102     if (config->is_dragscroll_enabled) {
    103         pointing_device_set_cpi(DILEMMA_DRAGSCROLL_DPI);
    104     } else if (config->is_sniping_enabled) {
    105         pointing_device_set_cpi(get_pointer_sniping_dpi(config));
    106     } else {
    107         pointing_device_set_cpi(get_pointer_default_dpi(config));
    108     }
    109 }
    110 
    111 /**
    112  * \brief Update the pointer's default DPI to the next or previous step.
    113  *
    114  * Increases the DPI value if `forward` is `true`, decreases it otherwise.
    115  * The increment/decrement steps are equal to DILEMMA_DEFAULT_DPI_CONFIG_STEP.
    116  */
    117 static void step_pointer_default_dpi(dilemma_config_t* config, bool forward) {
    118     config->pointer_default_dpi += forward ? 1 : -1;
    119     maybe_update_pointing_device_cpi(config);
    120 }
    121 
    122 /**
    123  * \brief Update the pointer's sniper-mode DPI to the next or previous step.
    124  *
    125  * Increases the DPI value if `forward` is `true`, decreases it otherwise.
    126  * The increment/decrement steps are equal to DILEMMA_SNIPING_DPI_CONFIG_STEP.
    127  */
    128 static void step_pointer_sniping_dpi(dilemma_config_t* config, bool forward) {
    129     config->pointer_sniping_dpi += forward ? 1 : -1;
    130     maybe_update_pointing_device_cpi(config);
    131 }
    132 
    133 uint16_t dilemma_get_pointer_default_dpi(void) {
    134     return get_pointer_default_dpi(&g_dilemma_config);
    135 }
    136 
    137 uint16_t dilemma_get_pointer_sniping_dpi(void) {
    138     return get_pointer_sniping_dpi(&g_dilemma_config);
    139 }
    140 
    141 void dilemma_cycle_pointer_default_dpi_noeeprom(bool forward) {
    142     step_pointer_default_dpi(&g_dilemma_config, forward);
    143 }
    144 
    145 void dilemma_cycle_pointer_default_dpi(bool forward) {
    146     step_pointer_default_dpi(&g_dilemma_config, forward);
    147     write_dilemma_config_to_eeprom(&g_dilemma_config);
    148 }
    149 
    150 void dilemma_cycle_pointer_sniping_dpi_noeeprom(bool forward) {
    151     step_pointer_sniping_dpi(&g_dilemma_config, forward);
    152 }
    153 
    154 void dilemma_cycle_pointer_sniping_dpi(bool forward) {
    155     step_pointer_sniping_dpi(&g_dilemma_config, forward);
    156     write_dilemma_config_to_eeprom(&g_dilemma_config);
    157 }
    158 
    159 bool dilemma_get_pointer_sniping_enabled(void) {
    160     return g_dilemma_config.is_sniping_enabled;
    161 }
    162 
    163 void dilemma_set_pointer_sniping_enabled(bool enable) {
    164     g_dilemma_config.is_sniping_enabled = enable;
    165     maybe_update_pointing_device_cpi(&g_dilemma_config);
    166 }
    167 
    168 bool dilemma_get_pointer_dragscroll_enabled(void) {
    169     return g_dilemma_config.is_dragscroll_enabled;
    170 }
    171 
    172 void dilemma_set_pointer_dragscroll_enabled(bool enable) {
    173     g_dilemma_config.is_dragscroll_enabled = enable;
    174     maybe_update_pointing_device_cpi(&g_dilemma_config);
    175 }
    176 
    177 void pointing_device_init_kb(void) {
    178     maybe_update_pointing_device_cpi(&g_dilemma_config);
    179 }
    180 
    181 /**
    182  * \brief Augment the pointing device behavior.
    183  *
    184  * Implement drag-scroll.
    185  */
    186 static void pointing_device_task_dilemma(report_mouse_t* mouse_report) {
    187     static int16_t scroll_buffer_x = 0;
    188     static int16_t scroll_buffer_y = 0;
    189     if (g_dilemma_config.is_dragscroll_enabled) {
    190 #    ifdef DILEMMA_DRAGSCROLL_REVERSE_X
    191         scroll_buffer_x -= mouse_report->x;
    192 #    else
    193         scroll_buffer_x += mouse_report->x;
    194 #    endif // DILEMMA_DRAGSCROLL_REVERSE_X
    195 #    ifdef DILEMMA_DRAGSCROLL_REVERSE_Y
    196         scroll_buffer_y -= mouse_report->y;
    197 #    else
    198         scroll_buffer_y += mouse_report->y;
    199 #    endif // DILEMMA_DRAGSCROLL_REVERSE_Y
    200         mouse_report->x = 0;
    201         mouse_report->y = 0;
    202         if (abs(scroll_buffer_x) > DILEMMA_DRAGSCROLL_BUFFER_SIZE) {
    203             mouse_report->h = scroll_buffer_x > 0 ? 1 : -1;
    204             scroll_buffer_x = 0;
    205         }
    206         if (abs(scroll_buffer_y) > DILEMMA_DRAGSCROLL_BUFFER_SIZE) {
    207             mouse_report->v = scroll_buffer_y > 0 ? 1 : -1;
    208             scroll_buffer_y = 0;
    209         }
    210     }
    211 }
    212 
    213 report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) {
    214     if (is_keyboard_master()) {
    215         pointing_device_task_dilemma(&mouse_report);
    216         mouse_report = pointing_device_task_user(mouse_report);
    217     }
    218     return mouse_report;
    219 }
    220 
    221 #    if defined(POINTING_DEVICE_ENABLE) && !defined(NO_DILEMMA_KEYCODES)
    222 /** \brief Whether SHIFT mod is enabled. */
    223 static bool has_shift_mod(void) {
    224 #        ifdef NO_ACTION_ONESHOT
    225     return mod_config(get_mods()) & MOD_MASK_SHIFT;
    226 #        else
    227     return mod_config(get_mods() | get_oneshot_mods()) & MOD_MASK_SHIFT;
    228 #        endif // NO_ACTION_ONESHOT
    229 }
    230 #    endif // POINTING_DEVICE_ENABLE && !NO_DILEMMA_KEYCODES
    231 
    232 /**
    233  * \brief Outputs the Dilemma configuration to console.
    234  *
    235  * Prints the in-memory configuration structure to console, for debugging.
    236  * Includes:
    237  *   - raw value
    238  *   - drag-scroll: on/off
    239  *   - sniping: on/off
    240  *   - default DPI: internal table index/actual DPI
    241  *   - sniping DPI: internal table index/actual DPI
    242  */
    243 static void debug_dilemma_config_to_console(dilemma_config_t* config) {
    244 #    ifdef CONSOLE_ENABLE
    245     dprintf("(dilemma) process_record_kb: config = {\n"
    246             "\traw = 0x%X,\n"
    247             "\t{\n"
    248             "\t\tis_dragscroll_enabled=%u\n"
    249             "\t\tis_sniping_enabled=%u\n"
    250             "\t\tdefault_dpi=0x%X (%u)\n"
    251             "\t\tsniping_dpi=0x%X (%u)\n"
    252             "\t}\n"
    253             "}\n",
    254             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));
    255 #    endif // CONSOLE_ENABLE
    256 }
    257 
    258 bool process_record_kb(uint16_t keycode, keyrecord_t* record) {
    259     if (!process_record_user(keycode, record)) {
    260         debug_dilemma_config_to_console(&g_dilemma_config);
    261         return false;
    262     }
    263 #    ifdef POINTING_DEVICE_ENABLE
    264 #        ifndef NO_DILEMMA_KEYCODES
    265     switch (keycode) {
    266         case POINTER_DEFAULT_DPI_FORWARD:
    267             if (record->event.pressed) {
    268                 // Step backward if shifted, forward otherwise.
    269                 dilemma_cycle_pointer_default_dpi(/* forward= */ !has_shift_mod());
    270             }
    271             break;
    272         case POINTER_DEFAULT_DPI_REVERSE:
    273             if (record->event.pressed) {
    274                 // Step forward if shifted, backward otherwise.
    275                 dilemma_cycle_pointer_default_dpi(/* forward= */ has_shift_mod());
    276             }
    277             break;
    278         case POINTER_SNIPING_DPI_FORWARD:
    279             if (record->event.pressed) {
    280                 // Step backward if shifted, forward otherwise.
    281                 dilemma_cycle_pointer_sniping_dpi(/* forward= */ !has_shift_mod());
    282             }
    283             break;
    284         case POINTER_SNIPING_DPI_REVERSE:
    285             if (record->event.pressed) {
    286                 // Step forward if shifted, backward otherwise.
    287                 dilemma_cycle_pointer_sniping_dpi(/* forward= */ has_shift_mod());
    288             }
    289             break;
    290         case SNIPING_MODE:
    291             dilemma_set_pointer_sniping_enabled(record->event.pressed);
    292             break;
    293         case SNIPING_MODE_TOGGLE:
    294             if (record->event.pressed) {
    295                 dilemma_set_pointer_sniping_enabled(!dilemma_get_pointer_sniping_enabled());
    296             }
    297             break;
    298         case DRAGSCROLL_MODE:
    299             dilemma_set_pointer_dragscroll_enabled(record->event.pressed);
    300             break;
    301         case DRAGSCROLL_MODE_TOGGLE:
    302             if (record->event.pressed) {
    303                 dilemma_set_pointer_dragscroll_enabled(!dilemma_get_pointer_dragscroll_enabled());
    304             }
    305             break;
    306     }
    307 #        endif // !NO_DILEMMA_KEYCODES
    308 #    endif     // POINTING_DEVICE_ENABLE
    309     debug_dilemma_config_to_console(&g_dilemma_config);
    310     if (IS_QK_KB(keycode) || IS_MOUSEKEY(keycode)) {
    311         debug_dilemma_config_to_console(&g_dilemma_config);
    312     }
    313     return true;
    314 }
    315 
    316 void eeconfig_init_kb(void) {
    317     g_dilemma_config.raw                 = 0;
    318     g_dilemma_config.pointer_default_dpi = 3; // DPI=1000
    319     write_dilemma_config_to_eeprom(&g_dilemma_config);
    320     maybe_update_pointing_device_cpi(&g_dilemma_config);
    321     eeconfig_init_user();
    322 }
    323 
    324 void matrix_init_kb(void) {
    325     read_dilemma_config_from_eeprom(&g_dilemma_config);
    326     matrix_init_user();
    327 }
    328 #endif // POINTING_DEVICE_ENABLE
    329 
    330 bool shutdown_kb(bool jump_to_bootloader) {
    331     if (!shutdown_user(jump_to_bootloader)) {
    332         return false;
    333     }
    334 #ifdef RGBLIGHT_ENABLE
    335     rgblight_enable_noeeprom();
    336     rgblight_mode_noeeprom(RGBLIGHT_MODE_STATIC_LIGHT);
    337     rgblight_setrgb(RGB_RED);
    338 #endif // RGBLIGHT_ENABLE
    339 #ifdef RGB_MATRIX_ENABLE
    340     rgb_matrix_set_color_all(RGB_RED);
    341     rgb_matrix_update_pwm_buffers();
    342 #endif // RGB_MATRIX_ENABLE
    343     return true;
    344 }