diff options
author | Christopher Hoage <[email protected]> | 2023-03-10 21:33:52 -0800 |
---|---|---|
committer | GitHub <[email protected]> | 2023-03-10 22:33:52 -0700 |
commit | 96b0baa230fb0672a68d1f75ba58613526fef0e7 (patch) | |
tree | 213d1bcfd434d410ad68046b31b0e2397a6ead13 /keyboards/spleeb/spleeb.c | |
parent | dbd1d3cba111dabf3b2d322a3681039cf46603f7 (diff) | |
download | qmk_firmware-96b0baa230fb0672a68d1f75ba58613526fef0e7.tar.gz qmk_firmware-96b0baa230fb0672a68d1f75ba58613526fef0e7.zip |
[Keyboard] Add Spleeb default and custom firmware (#19481)
Co-authored-by: jack <[email protected]>
Diffstat (limited to 'keyboards/spleeb/spleeb.c')
-rw-r--r-- | keyboards/spleeb/spleeb.c | 544 |
1 files changed, 544 insertions, 0 deletions
diff --git a/keyboards/spleeb/spleeb.c b/keyboards/spleeb/spleeb.c new file mode 100644 index 0000000000..658f30df75 --- /dev/null +++ b/keyboards/spleeb/spleeb.c @@ -0,0 +1,544 @@ +// Copyright 2022 Chris Hoage (@chrishoage) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "spleeb.h" +#include "transactions.h" + +#ifdef CONSOLE_ENABLE +# include "print.h" +#endif // CONSOLE_ENABLE + +#if defined(POINTING_DEVICE_ENABLE) || defined(SPLEEB_ENCODER_MODE_MAP_ENABLE) +typedef union { + uint16_t raw; + struct { + uint8_t pointer_default_dpi : 4; // 16 steps available. + uint8_t pointer_sniping_dpi : 2; // 4 steps available. + uint8_t enc_modes[NUM_ENCODERS]; + bool is_dragscroll_enabled : 1; + bool is_sniping_enabled : 1; + } __attribute__((packed)); +} spleeb_config_t; + +static spleeb_config_t g_spleeb_config = {0}; + +/** + * \brief Set the value of `config` from EEPROM. + * + * Note that `is_dragscroll_enabled` and `is_sniping_enabled` are purposefully + * ignored since we do not want to persist this state to memory. In practice, + * this state is always written to maximize write-performances. Therefore, we + * explicitly set them to `false` in this function. + */ +static void read_spleeb_config_from_eeprom(spleeb_config_t* config) { + config->raw = eeconfig_read_kb() & 0xffff; + config->is_dragscroll_enabled = false; + config->is_sniping_enabled = false; +} + +/** + * \brief Save the value of `config` to eeprom. + * + * Note that all values are written verbatim, including whether drag-scroll + * and/or sniper mode are enabled. `read_spleeb_config_from_eeprom(…)` + * resets these 2 values to `false` since it does not make sense to persist + * these across reboots of the board. + */ +static void write_spleeb_config_to_eeprom(spleeb_config_t* config) { + eeconfig_update_kb(config->raw); +} + +void eeconfig_init_kb(void) { + g_spleeb_config.raw = 0; + g_spleeb_config.pointer_default_dpi = 4; + +# ifdef SPLEEB_ENCODER_MODE_MAP_ENABLE + for (size_t i = 0; i < NUM_ENCODERS; i++) { + if (spleeb_encoder_mode_map[i][0].initalized) { + spleeb_enc_mode_t* first_enc_mode = &spleeb_encoder_mode_map[i][0]; + g_spleeb_config.enc_modes[i] = first_enc_mode->mode; + } + } +# endif // SPLEEB_ENCODER_MODE_MAP_ENABLE + + write_spleeb_config_to_eeprom(&g_spleeb_config); + eeconfig_init_user(); +} + +void matrix_init_kb(void) { + read_spleeb_config_from_eeprom(&g_spleeb_config); + matrix_init_user(); +} + +void spleeb_config_sync_handler(uint8_t initiator2target_buffer_size, const void* initiator2target_buffer, uint8_t target2initiator_buffer_size, void* target2initiator_buffer) { + if (initiator2target_buffer_size == sizeof(g_spleeb_config)) { + memcpy(&g_spleeb_config, initiator2target_buffer, sizeof(g_spleeb_config)); + } +} + +void keyboard_post_init_kb(void) { + transaction_register_rpc(RPC_ID_KB_CONFIG_SYNC, spleeb_config_sync_handler); + keyboard_post_init_user(); +} + +void housekeeping_task_kb(void) { + if (is_keyboard_master()) { + // Keep track of the last state, so that we can tell if we need to propagate to slave. + static spleeb_config_t last_spleeb_config = {0}; + static uint32_t last_sync = 0; + bool needs_sync = false; + + // Check if the state values are different. + if (memcmp(&g_spleeb_config, &last_spleeb_config, sizeof(g_spleeb_config))) { + needs_sync = true; + memcpy(&last_spleeb_config, &g_spleeb_config, sizeof(g_spleeb_config)); + } + // Send to slave every 500ms regardless of state change. + if (timer_elapsed32(last_sync) > 500) { + needs_sync = true; + } + + // Perform the sync if requested. + if (needs_sync) { + if (transaction_rpc_send(RPC_ID_KB_CONFIG_SYNC, sizeof(g_spleeb_config), &g_spleeb_config)) { + last_sync = timer_read32(); + } + } + } + // No need to invoke the user-specific callback, as it's been called + // already. +} +#endif // defined(POINTING_DEVICE_ENABLE) || defined(SPLEEB_ENCODER_MODE_MAP_ENABLE) + +#ifdef SPLEEB_ENCODER_MODE_MAP_ENABLE + +/** + * \brief Handle the encoder mode action when triggered by encoder_update_kb + * + * Weakly defined fuction intended to be overridden in a users keymap + */ +__attribute__((weak)) void spleeb_encoder_mode_trigger(uint8_t mode, bool clockwise) {} + +typedef struct { + uint8_t index; + spleeb_enc_mode_t* enc_mode; +} spleeb_found_enc_mode_t; + +static spleeb_found_enc_mode_t spleeb_get_found_encoder_mode(spleeb_config_t* config, uint8_t index) { + spleeb_found_enc_mode_t found_enc_mode; + + for (size_t i = 0; i < SPLEEB_ENCODER_MODE_COUNT; i++) { + spleeb_enc_mode_t* cur_enc_mode = &spleeb_encoder_mode_map[index][i]; + if (cur_enc_mode->mode == config->enc_modes[index]) { + found_enc_mode.index = i; + found_enc_mode.enc_mode = cur_enc_mode; + break; + } + } + + return found_enc_mode; +} + +/** + * \brief Step through the defined encoder modes for the encoder at the given + * index + * + * Step though the modes defined in spleeb_encoder_mode_map at the users keymap. + * Use a null terminator at the first character on the name property for the + * enc_mode struct to determine if we've reached the end of the defined encoder + * modes. When this happens loop back to the beginning. + */ +static void spleeb_step_encoder_mode(spleeb_config_t* config, uint8_t index) { + spleeb_found_enc_mode_t cur_enc_mode = spleeb_get_found_encoder_mode(config, index); + spleeb_enc_mode_t* next_enc_mode = &spleeb_encoder_mode_map[index][(cur_enc_mode.index + 1) % SPLEEB_ENCODER_MODE_COUNT]; + + if (!next_enc_mode->initalized) { + next_enc_mode = &spleeb_encoder_mode_map[index][0]; + } + + if (next_enc_mode->initalized) { + config->enc_modes[index] = next_enc_mode->mode; + write_spleeb_config_to_eeprom(config); + } +} + +bool encoder_update_kb(uint8_t index, bool clockwise) { + if (!encoder_update_user(index, clockwise)) { + return false; + } + + spleeb_encoder_mode_trigger(g_spleeb_config.enc_modes[index], clockwise); + + return true; +} +#endif // SPLEEB_ENCODER_MODE_MAP_ENABLE + +#ifdef POINTING_DEVICE_ENABLE + +/** \brief Return the current value of the pointer's default DPI. */ +static uint16_t get_pointer_default_dpi(spleeb_config_t* config) { + return (uint16_t)config->pointer_default_dpi * SPLEEB_DEFAULT_DPI_CONFIG_STEP + SPLEEB_MINIMUM_DEFAULT_DPI; +} + +/** \brief Return the current value of the pointer's sniper-mode DPI. */ +static uint16_t get_pointer_sniping_dpi(spleeb_config_t* config) { + return (uint16_t)config->pointer_sniping_dpi * SPLEEB_SNIPING_DPI_CONFIG_STEP + SPLEEB_MINIMUM_SNIPING_DPI; +} + +/** \brief Return the current value of the pointer's default DPI. */ +static uint16_t get_pointer_current_dpi(spleeb_config_t* config) { + if (config->is_sniping_enabled) { + return get_pointer_sniping_dpi(config); + } else { + return get_pointer_default_dpi(config); + } +} + +/** \brief Set the appropriate DPI for the input config. */ +static void maybe_update_pointing_device_cpi(spleeb_config_t* config) { + if (config->is_sniping_enabled) { + pointing_device_set_cpi(get_pointer_sniping_dpi(config)); + } else { + pointing_device_set_cpi(get_pointer_default_dpi(config)); + } +} + +/** + * \brief Update the pointer's default DPI to the next or previous step. + * + * Increases the DPI value if `forward` is `true`, decreases it otherwise. + * The increment/decrement steps are equal to SPLEEB_DEFAULT_DPI_CONFIG_STEP. + */ +static void step_pointer_default_dpi(spleeb_config_t* config, bool forward) { + config->pointer_default_dpi += forward ? 1 : -1; + maybe_update_pointing_device_cpi(config); +} + +/** + * \brief Update the pointer's sniper-mode DPI to the next or previous step. + * + * Increases the DPI value if `forward` is `true`, decreases it otherwise. + * The increment/decrement steps are equal to SPLEEB_SNIPING_DPI_CONFIG_STEP. + */ +static void step_pointer_sniping_dpi(spleeb_config_t* config, bool forward) { + config->pointer_sniping_dpi += forward ? 1 : -1; + maybe_update_pointing_device_cpi(config); +} + +uint16_t spleeb_get_pointer_default_dpi(void) { + return get_pointer_default_dpi(&g_spleeb_config); +} + +uint16_t spleeb_get_pointer_sniping_dpi(void) { + return get_pointer_sniping_dpi(&g_spleeb_config); +} + +void spleeb_cycle_pointer_default_dpi_noeeprom(bool forward) { + step_pointer_default_dpi(&g_spleeb_config, forward); +} + +void spleeb_cycle_pointer_default_dpi(bool forward) { + step_pointer_default_dpi(&g_spleeb_config, forward); + write_spleeb_config_to_eeprom(&g_spleeb_config); +} + +void spleeb_cycle_pointer_sniping_dpi_noeeprom(bool forward) { + step_pointer_sniping_dpi(&g_spleeb_config, forward); +} + +void spleeb_cycle_pointer_sniping_dpi(bool forward) { + step_pointer_sniping_dpi(&g_spleeb_config, forward); + write_spleeb_config_to_eeprom(&g_spleeb_config); +} + +bool spleeb_get_pointer_sniping_enabled(void) { + return g_spleeb_config.is_sniping_enabled; +} + +void spleeb_set_pointer_sniping_enabled(bool enable) { + g_spleeb_config.is_sniping_enabled = enable; + maybe_update_pointing_device_cpi(&g_spleeb_config); +} + +bool spleeb_get_pointer_dragscroll_enabled(void) { + return g_spleeb_config.is_dragscroll_enabled; +} + +void spleeb_set_pointer_dragscroll_enabled(bool enable) { + g_spleeb_config.is_dragscroll_enabled = enable; + cirque_pinnacle_enable_cursor_glide(enable); + maybe_update_pointing_device_cpi(&g_spleeb_config); +} +#endif // POINTING_DEVICE_ENABLE + +#ifdef POINTING_DEVICE_ENABLE +void pointing_device_init_kb(void) { + maybe_update_pointing_device_cpi(&g_spleeb_config); + + // only glide on drag scroll + cirque_pinnacle_enable_cursor_glide(false); + + set_auto_mouse_enable(true); + pointing_device_init_user(); +} + +/** + * \brief Augment the pointing device behavior. + * + * Drag-scroll implementation borrowed from https://github.com/qmk/qmk_firmware/pull/18218 + */ +static void pointing_device_task_spleeb(report_mouse_t* mouse_report) { + static int16_t scroll_x = 0; + static int16_t scroll_y = 0; + if (g_spleeb_config.is_dragscroll_enabled) { + scroll_x -= mouse_report->x; + scroll_y += mouse_report->y; + mouse_report->h = scroll_x / SPLEEB_DRAGSCROLL_DIVISOR; + mouse_report->v = scroll_y / SPLEEB_DRAGSCROLL_DIVISOR; + mouse_report->x = 0; + mouse_report->y = 0; + scroll_x -= (int16_t)mouse_report->h * SPLEEB_DRAGSCROLL_DIVISOR; + scroll_y -= (int16_t)mouse_report->v * SPLEEB_DRAGSCROLL_DIVISOR; + } +} + +report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) { + if (is_keyboard_master()) { + pointing_device_task_spleeb(&mouse_report); + + mouse_report = pointing_device_task_user(mouse_report); + } + + return mouse_report; +} + +/** + * \brief Outputs the Spleeb configuration to console. + * + * Prints the in-memory configuration structure to console, for debugging. + * Includes: + * - raw value + * - drag-scroll: on/off + * - sniping: on/off + * - default DPI: internal table index/actual DPI + * - sniping DPI: internal table index/actual DPI + */ +static void debug_spleeb_config_to_console(spleeb_config_t* config) { +# ifdef CONSOLE_ENABLE + pd_dprintf("(spleeb) process_record_kb: config = {\n" + "\traw = 0x%u,\n" + "\t{\n" + "\t\tis_dragscroll_enabled=%u\n" + "\t\tis_sniping_enabled=%u\n" + "\t\tdefault_dpi=0x%X (%u)\n" + "\t\tsniping_dpi=0x%X (%u)\n" + "\t}\n" + "}\n", + 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)); +# endif // CONSOLE_ENABLE +} +#endif // POINTING_DEVICE_ENABLE + +bool process_record_kb(uint16_t keycode, keyrecord_t* record) { + if (!process_record_user(keycode, record)) { +#ifdef POINTING_DEVICE_ENABLE + + debug_spleeb_config_to_console(&g_spleeb_config); +#endif // POINTING_DEVICE_ENABLE + return false; + } +#ifdef POINTING_DEVICE_ENABLE + switch (keycode) { + case POINTER_DEFAULT_DPI_FORWARD: + if (record->event.pressed) { + spleeb_cycle_pointer_default_dpi(true); + } + break; + case POINTER_DEFAULT_DPI_REVERSE: + if (record->event.pressed) { + spleeb_cycle_pointer_default_dpi(false); + } + break; + case POINTER_SNIPING_DPI_FORWARD: + if (record->event.pressed) { + spleeb_cycle_pointer_sniping_dpi(true); + } + break; + case POINTER_SNIPING_DPI_REVERSE: + if (record->event.pressed) { + spleeb_cycle_pointer_sniping_dpi(false); + } + break; + case SNIPING_MODE: + spleeb_set_pointer_sniping_enabled(record->event.pressed); + break; + case SNIPING_MODE_TOGGLE: + if (record->event.pressed) { + spleeb_set_pointer_sniping_enabled(!spleeb_get_pointer_sniping_enabled()); + } + break; + case DRAGSCROLL_MODE: + spleeb_set_pointer_dragscroll_enabled(record->event.pressed); + break; + case DRAGSCROLL_MODE_TOGGLE: + if (record->event.pressed) { + spleeb_set_pointer_dragscroll_enabled(!spleeb_get_pointer_dragscroll_enabled()); + } + break; + } +#endif // POINTING_DEVICE_ENABLE + +#ifdef SPLEEB_ENCODER_MODE_MAP_ENABLE + switch (keycode) { + case ENC_MODE_STEP_LEFT: + if (record->event.pressed) { + spleeb_step_encoder_mode(&g_spleeb_config, 0); + } + break; + case ENC_MODE_STEP_RIGHT: + if (record->event.pressed) { + spleeb_step_encoder_mode(&g_spleeb_config, 1); + } + break; + } +#endif // SPLEEB_ENCODER_MODE_MAP_ENABLE + +#ifdef POINTING_DEVICE_ENABLE + if ((keycode >= POINTER_DEFAULT_DPI_FORWARD && keycode <= ENC_MODE_STEP_RIGHT) || IS_MOUSEKEY(keycode)) { + debug_spleeb_config_to_console(&g_spleeb_config); + } +#endif // POINTING_DEVICE_ENABLE + + return true; +} + +#ifdef POINTING_DEVICE_ENABLE + +bool is_mouse_record_kb(uint16_t keycode, keyrecord_t* record) { + switch (keycode) { + case DRAGSCROLL_MODE: + case SNIPING_MODE: + return true; + default: + return false; + } + + return is_mouse_record_user(keycode, record); +} + +#endif // POINTING_DEVICE_ENABLE + +#ifdef OLED_ENABLE + +static void render_layer(void) { + oled_write_P(PSTR("LAYER: "), false); + + switch (get_highest_layer(layer_state)) { + case 0: + oled_write_ln_P(PSTR("\xC0\xC1"), false); + break; + case 1: + oled_write_ln_P(PSTR("\xC2\xC3"), false); + break; + case 2: + oled_write_ln_P(PSTR("\xC4\xC5"), false); + break; + case 3: + oled_write_ln_P(PSTR("\xC6\xC7"), false); + break; + case 4: + oled_write_ln_P(PSTR("\xC8\xC9"), false); + break; + case 5: + oled_write_ln_P(PSTR("\xCA\xCB"), false); + break; + default: + oled_write_ln_P(get_u8_str(get_highest_layer(layer_state) + 0x30, ' '), true); + } + + oled_write_ln_P("", false); +} + +static void render_mods(void) { + uint8_t modifiers = get_mods(); + + oled_write_ln_P(PSTR("MODS:"), false); + oled_write_ln_P("", false); + oled_write_P(PSTR("\325\326"), (modifiers & MOD_MASK_SHIFT)); + oled_write_P(PSTR("\327\330"), (modifiers & MOD_MASK_CTRL)); + oled_write_P(PSTR("\331\332"), (modifiers & MOD_MASK_ALT)); + oled_write_ln_P(PSTR("\333\334"), (modifiers & MOD_MASK_GUI)); + oled_write_ln_P("", false); +} + +static void render_lock(void) { + led_t led_state = host_keyboard_led_state(); + + oled_write_P(PSTR("LOCK: "), false); + oled_write_P(PSTR("\235\236"), led_state.caps_lock); + oled_write_ln_P(PSTR("\275\276"), led_state.num_lock); +} + +static void render_pointer(void) { +# ifdef POINTING_DEVICE_ENABLE + oled_write_ln_P(PSTR("POINTER:"), false); + oled_write_ln_P("", false); + oled_write_P(PSTR("dpi:"), false); + oled_write_ln_P(get_u16_str(get_pointer_current_dpi(&g_spleeb_config), ' '), false); + oled_write_ln_P("", false); +# endif // POINTING_DEVICE_ENABLE +} + +# ifdef SPLEEB_ENCODER_MODE_MAP_ENABLE +static uint8_t spleeb_get_encoder_mode(spleeb_config_t* config, uint8_t index) { + spleeb_found_enc_mode_t found_enc_mode = spleeb_get_found_encoder_mode(config, index); + return found_enc_mode.enc_mode->mode; +} + +/** + * \brief Map an encoder mode to a string to be displayed on the OLED + * + * Weakly defined fuction intended to be overridden in a users keymap. My be + * omitted if no OLED is used. + */ +__attribute__((weak)) const char* spleeb_encoder_mode_string(uint8_t mode) { + return get_u8_str(mode, ' '); +} +# endif // SPLEEB_ENCODER_MODE_MAP_ENABLE + +static void render_encoder(void) { +# ifdef SPLEEB_ENCODER_MODE_MAP_ENABLE + oled_write_ln_P(PSTR("ENCODER:"), false); + oled_write_ln_P("", false); + oled_write_P(PSTR("R: "), false); + oled_write_ln_P(spleeb_encoder_mode_string(spleeb_get_encoder_mode(&g_spleeb_config, 1)), false); + oled_write_P(PSTR("L: "), false); + oled_write_ln_P(spleeb_encoder_mode_string(spleeb_get_encoder_mode(&g_spleeb_config, 0)), false); +# endif // SPLEEB_ENCODER_MODE_MAP_ENABLE +} + +static void render_status(void) { + render_layer(); + render_mods(); + render_lock(); + render_pointer(); + render_encoder(); +} + +oled_rotation_t oled_init_kb(oled_rotation_t rotation) { + return OLED_ROTATION_90; +} + +bool oled_task_kb(void) { + if (is_keyboard_master()) { + return false; + } + + if (!oled_task_user()) { + return false; + } + + render_status(); + return false; +} +#endif // OLED_ENABLE |