diff options
author | Andrew Kannan <andrew.kannan@gmail.com> | 2024-02-26 17:16:00 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-27 09:16:00 +1100 |
commit | 490641307ab30ae10dd80fde766f5988bd154ca2 (patch) | |
tree | 66e0d722cd75b5d986abbdf61bb36e8a201ed088 /keyboards/cannonkeys/lib | |
parent | 0f701c7dbecc0c90b8e5d74ce81c2575ba0c4144 (diff) |
Cleanup Satisfaction75 Firmware and add new revisions (#22082)
Co-authored-by: Ryan <fauxpark@gmail.com>
Co-authored-by: Nick Brassel <nick@tzarc.org>
Diffstat (limited to 'keyboards/cannonkeys/lib')
5 files changed, 1012 insertions, 0 deletions
diff --git a/keyboards/cannonkeys/lib/satisfaction75/satisfaction_core.c b/keyboards/cannonkeys/lib/satisfaction75/satisfaction_core.c new file mode 100644 index 0000000000..ce9422c5a8 --- /dev/null +++ b/keyboards/cannonkeys/lib/satisfaction75/satisfaction_core.c @@ -0,0 +1,391 @@ +// Copyright 2023 Andrew Kannan +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "satisfaction_core.h" +#include "print.h" +#include "debug.h" + +#include <ch.h> +#include <hal.h> + +#include "timer.h" + +#include "raw_hid.h" +#include "dynamic_keymap.h" +#include "eeprom.h" +#include "version.h" // for QMK_BUILDDATE used in EEPROM magic + +/* Artificial delay added to get media keys to work in the encoder*/ +#define MEDIA_KEY_DELAY 10 + +volatile uint8_t led_numlock = false; +volatile uint8_t led_capslock = false; +volatile uint8_t led_scrolllock = false; + +uint8_t layer; + +bool clock_set_mode = false; +uint8_t oled_mode = OLED_DEFAULT; +bool oled_repaint_requested = false; +bool oled_wakeup_requested = false; +uint32_t oled_sleep_timer; + +uint8_t encoder_value = 32; +uint8_t encoder_mode = ENC_MODE_VOLUME; +uint8_t enabled_encoder_modes = 0x1F; + +RTCDateTime last_timespec; +uint16_t last_minute = 0; + +uint8_t time_config_idx = 0; +int8_t hour_config = 0; +int16_t minute_config = 0; +int8_t year_config = 0; +int8_t month_config = 0; +int8_t day_config = 0; +uint8_t previous_encoder_mode = 0; + +void board_init(void) { + SYSCFG->CFGR1 |= SYSCFG_CFGR1_I2C1_DMA_RMP; + SYSCFG->CFGR1 &= ~(SYSCFG_CFGR1_SPI2_DMA_RMP); +} + +void keyboard_post_init_kb(){ + /* + This is a workaround to some really weird behavior + Without this code, the OLED will turn on, but not when you initially plug the keyboard in. + You have to manually trigger a user reset to get the OLED to initialize properly + I'm not sure what the root cause is at this time, but this workaround fixes it. + */ + #ifdef OLED_ENABLE + if(!is_oled_on()){ + wait_ms(3000); + oled_init(OLED_ROTATION_0); + } + #endif +} + +#ifdef VIA_ENABLE +void custom_set_value(uint8_t *data) { + uint8_t *value_id = &(data[0]); + uint8_t *value_data = &(data[1]); + + switch ( *value_id ) { + case id_oled_default_mode: + { + eeprom_update_byte((uint8_t*)EEPROM_DEFAULT_OLED, value_data[0]); + break; + } + case id_oled_mode: + { + oled_mode = value_data[0]; + oled_request_wakeup(); + break; + } + case id_encoder_modes: + { + uint8_t index = value_data[0]; + uint8_t enable = value_data[1]; + enabled_encoder_modes = (enabled_encoder_modes & ~(1<<index)) | (enable<<index); + eeprom_update_byte((uint8_t*)EEPROM_ENABLED_ENCODER_MODES, enabled_encoder_modes); + break; + } + case id_encoder_custom: + { + uint8_t custom_encoder_idx = value_data[0]; + uint8_t encoder_behavior = value_data[1]; + uint16_t keycode = (value_data[2] << 8) | value_data[3]; + set_custom_encoder_config(custom_encoder_idx, encoder_behavior, keycode); + break; + } + } +} + +void custom_get_value(uint8_t *data) { + uint8_t *value_id = &(data[0]); + uint8_t *value_data = &(data[1]); + + switch ( *value_id ) { + case id_oled_default_mode: + { + uint8_t default_oled = eeprom_read_byte((uint8_t*)EEPROM_DEFAULT_OLED); + value_data[0] = default_oled; + break; + } + case id_oled_mode: + { + value_data[0] = oled_mode; + break; + } + case id_encoder_modes: + { + uint8_t index = value_data[0]; + value_data[1] = (enabled_encoder_modes & (1<<index)) ? 1 : 0; + break; + } + case id_encoder_custom: + { + uint8_t custom_encoder_idx = value_data[0]; + uint8_t encoder_behavior = value_data[1]; + uint16_t keycode = retrieve_custom_encoder_config(custom_encoder_idx, encoder_behavior); + value_data[2] = keycode >> 8; + value_data[3] = keycode & 0xFF; + break; + } + } +} + +void via_custom_value_command_kb(uint8_t *data, uint8_t length) { + uint8_t *command_id = &(data[0]); + uint8_t *channel_id = &(data[1]); + uint8_t *value_id_and_data = &(data[2]); + + if ( *channel_id == id_custom_channel ) { + switch ( *command_id ) + { + case id_custom_set_value: + { + custom_set_value(value_id_and_data); + break; + } + case id_custom_get_value: + { + custom_get_value(value_id_and_data); + break; + } + case id_custom_save: + { + // values are saved in custom_set_value() + break; + } + default: + { + // Unhandled message. + *command_id = id_unhandled; + break; + } + } + return; + } + + *command_id = id_unhandled; + + // DO NOT call raw_hid_send(data,length) here, let caller do this +} +#endif + + +void read_host_led_state(void) { + led_t led_state = host_keyboard_led_state(); + if (led_state.num_lock) { + if (led_numlock == false){ + led_numlock = true;} + } else { + if (led_numlock == true){ + led_numlock = false;} + } + if (led_state.caps_lock) { + if (led_capslock == false){ + led_capslock = true;} + } else { + if (led_capslock == true){ + led_capslock = false;} + } + if (led_state.scroll_lock) { + if (led_scrolllock == false){ + led_scrolllock = true;} + } else { + if (led_scrolllock == true){ + led_scrolllock = false;} + } +} + +layer_state_t layer_state_set_kb(layer_state_t state) { + state = layer_state_set_user(state); + layer = get_highest_layer(state); + oled_request_wakeup(); + return state; +} + +bool process_record_kb(uint16_t keycode, keyrecord_t *record) { + oled_request_wakeup(); + switch (keycode) { + case OLED_TOGG: + if(!clock_set_mode){ + if (record->event.pressed) { + oled_mode = (oled_mode + 1) % _NUM_OLED_MODES; + } + } + return false; + case CLOCK_SET: + if (record->event.pressed) { + if(clock_set_mode){ + pre_encoder_mode_change(); + clock_set_mode = false; + encoder_mode = previous_encoder_mode; + post_encoder_mode_change(); + + }else{ + previous_encoder_mode = encoder_mode; + pre_encoder_mode_change(); + clock_set_mode = true; + encoder_mode = ENC_MODE_CLOCK_SET; + post_encoder_mode_change(); + } + } + return false; + case ENC_PRESS: + if (record->event.pressed) { + uint16_t mapped_code = handle_encoder_press(); + uint16_t held_keycode_timer = timer_read(); + if(mapped_code != 0){ + register_code16(mapped_code); + while (timer_elapsed(held_keycode_timer) < MEDIA_KEY_DELAY){ /* no-op */ } + unregister_code16(mapped_code); + } + } else { + // Do something else when release + } + return false; + default: + break; + } + + return process_record_user(keycode, record); +} + + +bool encoder_update_kb(uint8_t index, bool clockwise) { + if (!encoder_update_user(index, clockwise)) return false; + oled_request_wakeup(); + encoder_value = (encoder_value + (clockwise ? 1 : -1)) % 64; + if (index == 0) { + if (layer == 0){ + uint16_t mapped_code = 0; + if (clockwise) { + mapped_code = handle_encoder_clockwise(); + } else { + mapped_code = handle_encoder_ccw(); + } + uint16_t held_keycode_timer = timer_read(); + if(mapped_code != 0){ + register_code16(mapped_code); + while (timer_elapsed(held_keycode_timer) < MEDIA_KEY_DELAY){ /* no-op */ } + unregister_code16(mapped_code); + } + } else { + if(clockwise){ + change_encoder_mode(false); + } else { + change_encoder_mode(true); + } + } + } + return true; +} + +void custom_config_reset(void){ + void *p = (void*)(VIA_EEPROM_CUSTOM_CONFIG_ADDR); + void *end = (void*)(VIA_EEPROM_CUSTOM_CONFIG_ADDR+VIA_EEPROM_CUSTOM_CONFIG_SIZE); + while ( p != end ) { + eeprom_update_byte(p, 0); + ++p; + } + eeprom_update_byte((uint8_t*)EEPROM_ENABLED_ENCODER_MODES, 0x1F); +} + +void custom_config_load(void){ +#ifdef DYNAMIC_KEYMAP_ENABLE + oled_mode = eeprom_read_byte((uint8_t*)EEPROM_DEFAULT_OLED); + enabled_encoder_modes = eeprom_read_byte((uint8_t*)EEPROM_ENABLED_ENCODER_MODES); +#endif +} + +// Called from via_init() if VIA_ENABLE +// Called from matrix_init_kb() if not VIA_ENABLE +void via_init_kb(void) +{ + // This checks both an EEPROM reset (from bootmagic lite, keycodes) + // and also firmware build date (from via_eeprom_is_valid()) + if (eeconfig_is_enabled()) { + custom_config_load(); + } else { +#ifdef DYNAMIC_KEYMAP_ENABLE + // Reset the custom stuff + custom_config_reset(); +#endif + // DO NOT set EEPROM valid here, let caller do this + } +} + +void matrix_init_kb(void) +{ +#ifndef VIA_ENABLE + via_init_kb(); + via_eeprom_set_valid(true); +#endif // VIA_ENABLE + + rtcGetTime(&RTCD1, &last_timespec); + matrix_init_user(); + oled_request_wakeup(); +} + + +void housekeeping_task_kb(void) { + rtcGetTime(&RTCD1, &last_timespec); + uint16_t minutes_since_midnight = last_timespec.millisecond / 1000 / 60; + + if (minutes_since_midnight != last_minute){ + last_minute = minutes_since_midnight; + oled_request_repaint(); + } +} + +// +// In the case of VIA being disabled, we still need to check if +// keyboard level EEPROM memory is valid before loading. +// Thus these are copies of the same functions in VIA, since +// the backlight settings reuse VIA's EEPROM magic/version, +// and the ones in via.c won't be compiled in. +// +// Yes, this is sub-optimal, and is only here for completeness +// (i.e. catering to the 1% of people that want wilba.tech LED bling +// AND want persistent settings BUT DON'T want to use dynamic keymaps/VIA). +// +#ifndef VIA_ENABLE + +bool via_eeprom_is_valid(void) +{ + char *p = QMK_BUILDDATE; // e.g. "2019-11-05-11:29:54" + uint8_t magic0 = ( ( p[2] & 0x0F ) << 4 ) | ( p[3] & 0x0F ); + uint8_t magic1 = ( ( p[5] & 0x0F ) << 4 ) | ( p[6] & 0x0F ); + uint8_t magic2 = ( ( p[8] & 0x0F ) << 4 ) | ( p[9] & 0x0F ); + + return (eeprom_read_byte( (void*)VIA_EEPROM_MAGIC_ADDR+0 ) == magic0 && + eeprom_read_byte( (void*)VIA_EEPROM_MAGIC_ADDR+1 ) == magic1 && + eeprom_read_byte( (void*)VIA_EEPROM_MAGIC_ADDR+2 ) == magic2 ); +} + +// Sets VIA/keyboard level usage of EEPROM to valid/invalid +// Keyboard level code (eg. via_init_kb()) should not call this +void via_eeprom_set_valid(bool valid) +{ + char *p = QMK_BUILDDATE; // e.g. "2019-11-05-11:29:54" + uint8_t magic0 = ( ( p[2] & 0x0F ) << 4 ) | ( p[3] & 0x0F ); + uint8_t magic1 = ( ( p[5] & 0x0F ) << 4 ) | ( p[6] & 0x0F ); + uint8_t magic2 = ( ( p[8] & 0x0F ) << 4 ) | ( p[9] & 0x0F ); + + eeprom_update_byte( (void*)VIA_EEPROM_MAGIC_ADDR+0, valid ? magic0 : 0xFF); + eeprom_update_byte( (void*)VIA_EEPROM_MAGIC_ADDR+1, valid ? magic1 : 0xFF); + eeprom_update_byte( (void*)VIA_EEPROM_MAGIC_ADDR+2, valid ? magic2 : 0xFF); +} + +void via_eeprom_reset(void) +{ + // Set the VIA specific EEPROM state as invalid. + via_eeprom_set_valid(false); + // Set the TMK/QMK EEPROM state as invalid. + eeconfig_disable(); +} + +#endif // VIA_ENABLE diff --git a/keyboards/cannonkeys/lib/satisfaction75/satisfaction_core.h b/keyboards/cannonkeys/lib/satisfaction75/satisfaction_core.h new file mode 100644 index 0000000000..30caeadc38 --- /dev/null +++ b/keyboards/cannonkeys/lib/satisfaction75/satisfaction_core.h @@ -0,0 +1,101 @@ +// Copyright 2023 Andrew Kannan +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "quantum.h" + +#include "via.h" // only for EEPROM address +#include "satisfaction_keycodes.h" + +#define EEPROM_ENABLED_ENCODER_MODES (VIA_EEPROM_CUSTOM_CONFIG_ADDR) +#define EEPROM_DEFAULT_OLED (VIA_EEPROM_CUSTOM_CONFIG_ADDR+1) +#define EEPROM_CUSTOM_ENCODER (VIA_EEPROM_CUSTOM_CONFIG_ADDR+2) + +enum s75_keyboard_value_id { + id_encoder_modes = 1, + id_oled_default_mode, + id_encoder_custom, + id_oled_mode +}; + +enum encoder_modes { + ENC_MODE_VOLUME, + ENC_MODE_MEDIA, + ENC_MODE_SCROLL, + ENC_MODE_BRIGHTNESS, + ENC_MODE_BACKLIGHT, + ENC_MODE_CUSTOM0, + ENC_MODE_CUSTOM1, + ENC_MODE_CUSTOM2, + _NUM_ENCODER_MODES, + ENC_MODE_CLOCK_SET // This shouldn't be included in the default modes, so we put it after NUM_ENCODER_MODES +}; + +enum custom_encoder_behavior { + ENC_CUSTOM_CW = 0, + ENC_CUSTOM_CCW, + ENC_CUSTOM_PRESS +}; + +enum oled_modes { + OLED_DEFAULT, + OLED_TIME, + OLED_OFF, + _NUM_OLED_MODES +}; + + +// Keyboard Information +extern volatile uint8_t led_numlock; +extern volatile uint8_t led_capslock; +extern volatile uint8_t led_scrolllock; +extern uint8_t layer; + +// OLED Behavior +extern uint8_t oled_mode; +extern bool oled_repaint_requested; +extern bool oled_wakeup_requested; +extern uint32_t oled_sleep_timer; + +// Encoder Behavior +extern uint8_t encoder_value; +extern uint8_t encoder_mode; +extern uint8_t enabled_encoder_modes; + +// RTC +extern RTCDateTime last_timespec; +extern uint16_t last_minute; + +// RTC Configuration +extern bool clock_set_mode; +extern uint8_t time_config_idx; +extern int8_t hour_config; +extern int16_t minute_config; +extern int8_t year_config; +extern int8_t month_config; +extern int8_t day_config; +extern uint8_t previous_encoder_mode; + +// Backlighting +#ifdef BACKLIGHT_ENABLE +extern backlight_config_t kb_backlight_config; +extern bool kb_backlight_breathing; +#endif + +void pre_encoder_mode_change(void); +void post_encoder_mode_change(void); +void change_encoder_mode(bool negative); +uint16_t handle_encoder_clockwise(void); +uint16_t handle_encoder_ccw(void); +uint16_t handle_encoder_press(void); +uint16_t retrieve_custom_encoder_config(uint8_t encoder_idx, uint8_t behavior); +void set_custom_encoder_config(uint8_t encoder_idx, uint8_t behavior, uint16_t new_code); + +void update_time_config(int8_t increment); + +void oled_request_wakeup(void); +void oled_request_repaint(void); +bool oled_task_needs_to_repaint(void); + +void custom_config_load(void); diff --git a/keyboards/cannonkeys/lib/satisfaction75/satisfaction_encoder.c b/keyboards/cannonkeys/lib/satisfaction75/satisfaction_encoder.c new file mode 100644 index 0000000000..7122091ea3 --- /dev/null +++ b/keyboards/cannonkeys/lib/satisfaction75/satisfaction_encoder.c @@ -0,0 +1,231 @@ +// Copyright 2023 Andrew Kannan +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "satisfaction_core.h" +#include "eeprom.h" + +void pre_encoder_mode_change(void){ + if(encoder_mode == ENC_MODE_CLOCK_SET){ + RTCDateTime timespec; + timespec.year = year_config; + timespec.month = month_config; + timespec.day = day_config; + // timespec.dayofweek = last_timespec.dayofweek; + // timespec.dstflag = last_timespec.dstflag; + timespec.millisecond = (hour_config * 60 + minute_config) * 60 * 1000; + rtcSetTime(&RTCD1, ×pec); + } +} + +void post_encoder_mode_change(void){ + if(encoder_mode == ENC_MODE_CLOCK_SET){ + hour_config = (last_minute / 60); + minute_config = last_minute % 60; + year_config = last_timespec.year; + month_config = last_timespec.month; + day_config = last_timespec.day; + time_config_idx = 0; + } +} + +void change_encoder_mode(bool negative){ + pre_encoder_mode_change(); + if(enabled_encoder_modes == 0){ + enabled_encoder_modes = 0x1F; + } + do { + if(negative){ + if (encoder_mode == 0){ + encoder_mode = _NUM_ENCODER_MODES - 1; + } else{ + encoder_mode = encoder_mode - 1; + } + } else { + encoder_mode = (encoder_mode + 1) % _NUM_ENCODER_MODES; + } + } while(((1 << encoder_mode) & enabled_encoder_modes) == 0); + post_encoder_mode_change(); +} + +void update_time_config(int8_t increment){ + uint8_t day_limit = 31; + uint16_t adjusted_year = 1980 + year_config; + switch(time_config_idx){ + case 0: // hour + default: + hour_config = (hour_config + increment) % 24; + if (hour_config < 0){ + hour_config += 24; + } + break; + case 1: // minute + minute_config = (minute_config + increment) % 60; + if (minute_config < 0){ + minute_config += 60; + } + break; + case 2: // year + year_config += increment; + break; + case 3: // month + month_config = (month_config % 12) + increment; + if (month_config <= 0){ + month_config += 12; + } + break; + case 4: //day + if (month_config == 9 || month_config == 4 || month_config == 6 || month_config == 11){ + day_limit = 30; + } else if(month_config == 2){ + day_limit = adjusted_year % 4 == 0 && !(adjusted_year % 100 == 0 && adjusted_year % 400 != 0) ? 29 : 28; + } + day_config = (day_config % day_limit) + increment; + if(day_config <= 0){ + day_config += day_limit; + } + break; + } +} + +uint16_t handle_encoder_clockwise(void){ + uint16_t mapped_code = 0; + switch(encoder_mode){ + default: + case ENC_MODE_VOLUME: + mapped_code = KC_VOLU; + break; + case ENC_MODE_MEDIA: + mapped_code = KC_MEDIA_NEXT_TRACK; + break; + case ENC_MODE_SCROLL: + mapped_code = KC_WH_D; + break; +#ifdef BACKLIGHT_ENABLE + case ENC_MODE_BACKLIGHT: + backlight_increase(); + if(get_backlight_level() != 0){ + backlight_enable(); + } + break; +#endif + case ENC_MODE_BRIGHTNESS: + mapped_code = KC_BRIGHTNESS_UP; + break; +#ifdef DYNAMIC_KEYMAP_ENABLE + case ENC_MODE_CUSTOM0: + mapped_code = retrieve_custom_encoder_config(0, ENC_CUSTOM_CW); + break; + case ENC_MODE_CUSTOM1: + mapped_code = retrieve_custom_encoder_config(1, ENC_CUSTOM_CW); + break; + case ENC_MODE_CUSTOM2: + mapped_code = retrieve_custom_encoder_config(2, ENC_CUSTOM_CW); + break; +#endif + case ENC_MODE_CLOCK_SET: + update_time_config(1); + break; + } + return mapped_code; +} + +uint16_t handle_encoder_ccw(void){ + uint16_t mapped_code = 0; + switch(encoder_mode){ + default: + case ENC_MODE_VOLUME: + mapped_code = KC_VOLD; + break; + case ENC_MODE_MEDIA: + mapped_code = KC_MEDIA_PREV_TRACK; + break; + case ENC_MODE_SCROLL: + mapped_code = KC_WH_U; + break; +#ifdef BACKLIGHT_ENABLE + case ENC_MODE_BACKLIGHT: + backlight_decrease(); + if(get_backlight_level() == 0){ + backlight_disable(); + } + break; +#endif + case ENC_MODE_BRIGHTNESS: + mapped_code = KC_BRIGHTNESS_DOWN; + break; +#ifdef DYNAMIC_KEYMAP_ENABLE + case ENC_MODE_CUSTOM0: + mapped_code = retrieve_custom_encoder_config(0, ENC_CUSTOM_CCW); + break; + case ENC_MODE_CUSTOM1: + mapped_code = retrieve_custom_encoder_config(1, ENC_CUSTOM_CCW); + break; + case ENC_MODE_CUSTOM2: + mapped_code = retrieve_custom_encoder_config(2, ENC_CUSTOM_CCW); + break; +#endif + + case ENC_MODE_CLOCK_SET: + update_time_config(-1); + break; + } + return mapped_code; +} + +uint16_t handle_encoder_press(void){ + uint16_t mapped_code = 0; + switch(encoder_mode){ + case ENC_MODE_VOLUME: + mapped_code = KC_MUTE; + break; + default: + case ENC_MODE_MEDIA: + mapped_code = KC_MEDIA_PLAY_PAUSE; + break; + case ENC_MODE_SCROLL: + mapped_code = KC_BTN3; + break; +#ifdef BACKLIGHT_ENABLE + case ENC_MODE_BACKLIGHT: + breathing_toggle(); + break; +#endif +#ifdef DYNAMIC_KEYMAP_ENABLE + case ENC_MODE_CUSTOM0: + mapped_code = retrieve_custom_encoder_config(0, ENC_CUSTOM_PRESS); + break; + case ENC_MODE_CUSTOM1: + mapped_code = retrieve_custom_encoder_config(1, ENC_CUSTOM_PRESS); + break; + case ENC_MODE_CUSTOM2: + mapped_code = retrieve_custom_encoder_config(2, ENC_CUSTOM_PRESS); + break; +#endif + case ENC_MODE_CLOCK_SET: + time_config_idx = (time_config_idx + 1) % 5; + case ENC_MODE_BRIGHTNESS: + break; + } + return mapped_code; +} + + +uint16_t retrieve_custom_encoder_config(uint8_t encoder_idx, uint8_t behavior){ +#ifdef DYNAMIC_KEYMAP_ENABLE + void* addr = (void*)(EEPROM_CUSTOM_ENCODER + (encoder_idx * 6) + (behavior * 2)); + //big endian + uint16_t keycode = eeprom_read_byte(addr) << 8; + keycode |= eeprom_read_byte(addr + 1); + return keycode; +#else + return 0; +#endif +} + +void set_custom_encoder_config(uint8_t encoder_idx, uint8_t behavior, uint16_t new_code){ +#ifdef DYNAMIC_KEYMAP_ENABLE + void* addr = (void*)(EEPROM_CUSTOM_ENCODER + (encoder_idx * 6) + (behavior * 2)); + eeprom_update_byte(addr, (uint8_t)(new_code >> 8)); + eeprom_update_byte(addr + 1, (uint8_t)(new_code & 0xFF)); +#endif +} diff --git a/keyboards/cannonkeys/lib/satisfaction75/satisfaction_keycodes.h b/keyboards/cannonkeys/lib/satisfaction75/satisfaction_keycodes.h new file mode 100644 index 0000000000..53f9facfc8 --- /dev/null +++ b/keyboards/cannonkeys/lib/satisfaction75/satisfaction_keycodes.h @@ -0,0 +1,10 @@ +// Copyright 2023 Andrew Kannan +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +enum my_keycodes { + ENC_PRESS = QK_KB_0, + CLOCK_SET, + OLED_TOGG +}; diff --git a/keyboards/cannonkeys/lib/satisfaction75/satisfaction_oled.c b/keyboards/cannonkeys/lib/satisfaction75/satisfaction_oled.c new file mode 100644 index 0000000000..18ae368e53 --- /dev/null +++ b/keyboards/cannonkeys/lib/satisfaction75/satisfaction_oled.c @@ -0,0 +1,279 @@ +// Copyright 2023 Andrew Kannan +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "satisfaction_core.h" +#include <stdio.h> + +void draw_default(void); +void draw_clock(void); + +#ifdef OLED_ENABLE + +oled_rotation_t oled_init_kb(oled_rotation_t rotation) { return OLED_ROTATION_0; } + +bool oled_task_kb(void) { + if (!oled_task_user()) { return false; } + if (!oled_task_needs_to_repaint()) { + return false; + } + oled_clear(); + if (clock_set_mode) { + draw_clock(); + return false; + } + switch (oled_mode) { + default: + case OLED_DEFAULT: + draw_default(); + break; + case OLED_TIME: + draw_clock(); + break; + } + return false; +} + +// Request a repaint of the OLED image without resetting the OLED sleep timer. +// Used for things like clock updates that should not keep the OLED turned on +// if there is no other activity. +void oled_request_repaint(void) { + if (is_oled_on()) { + oled_repaint_requested = true; + } +} + +// Request a repaint of the OLED image and reset the OLED sleep timer. +// Needs to be called after any activity that should keep the OLED turned on. +void oled_request_wakeup(void) { + oled_wakeup_requested = true; +} + +// Check whether oled_task_user() needs to repaint the OLED image. This +// function should be called at the start of oled_task_user(); it also handles +// the OLED sleep timer and the OLED_OFF mode. +bool oled_task_needs_to_repaint(void) { + // In the OLED_OFF mode the OLED is kept turned off; any wakeup requests + // are ignored. + if ((oled_mode == OLED_OFF) && !clock_set_mode) { + oled_wakeup_requested = false; + oled_repaint_requested = false; + oled_off(); + return false; + } + + // If OLED wakeup was requested, reset the sleep timer and do a repaint. + if (oled_wakeup_requested) { + oled_wakeup_requested = false; + oled_repaint_requested = false; + oled_sleep_timer = timer_read32() + CUSTOM_OLED_TIMEOUT; + oled_on(); + return true; + } + + // If OLED repaint was requested, just do a repaint without touching the + // sleep timer. + if (oled_repaint_requested) { + oled_repaint_requested = false; + return true; + } + + // If the OLED is currently off, skip the repaint (which would turn the + // OLED on if the image is changed in any way). + if (!is_oled_on()) { + return false; + } + + // If the sleep timer has expired while the OLED was on, turn the OLED off. + if (timer_expired32(timer_read32(), oled_sleep_timer)) { + oled_off(); + return false; + } + + // Always perform a repaint if the OLED is currently on. (This can + // potentially be optimized to avoid unneeded repaints if all possible + // state changes are covered by oled_request_repaint() or + // oled_request_wakeup(), but then any missed calls to these functions + // would result in displaying a stale image.) + return true; +} + + +static void draw_line_h(uint8_t x, uint8_t y, uint8_t len) { + for (uint8_t i = 0; i < len; i++) { + oled_write_pixel(i + x, y, true); + } +} + +static void draw_line_v(uint8_t x, uint8_t y, uint8_t len) { + for (uint8_t i = 0; i < len; i++) { + oled_write_pixel(x, i + y, true); + } +} + +static char* get_enc_mode(void) { + switch (encoder_mode) { + default: + case ENC_MODE_VOLUME: + return "VOL"; + case ENC_MODE_MEDIA: + return "MED"; + case ENC_MODE_SCROLL: + return "SCR"; + case ENC_MODE_BRIGHTNESS: + return "BRT"; + case ENC_MODE_BACKLIGHT: + return "BKL"; + case ENC_MODE_CLOCK_SET: + return "CLK"; + case ENC_MODE_CUSTOM0: + return "CS0"; + case ENC_MODE_CUSTOM1: + return "CS1"; + case ENC_MODE_CUSTOM2: + return "CS2"; + } +} + +static char* get_time(void) { + uint8_t hour = last_minute / 60; + uint16_t minute = last_minute % 60; + + if (encoder_mode == ENC_MODE_CLOCK_SET) { + hour = hour_config; + minute = minute_config; + } + + bool is_pm = (hour / 12) > 0; + hour = hour % 12; + if (hour == 0) { + hour = 12; + } + + static char time_str[8] = ""; + snprintf(time_str, sizeof(time_str), "%02hhu:%02hu%s", hour, minute, is_pm ? "pm" : "am"); + + return time_str; +} + +static char* get_date(void) { + int16_t year = last_timespec.year + 1980; + int8_t month = last_timespec.month; + int8_t day = last_timespec.day; + + if (encoder_mode == ENC_MODE_CLOCK_SET) { + year = year_config + 1980; + month = month_config; + day = day_config; + } + + static char date_str[11] = ""; + snprintf(date_str, sizeof(date_str), "%04hd-%02hhd-%02hhd", year, month, day); + + return date_str; +} + +void draw_default(void) { + oled_write_P(PSTR("LAYER "), false); + oled_write_char(get_highest_layer(layer_state) + 0x30, true); + + oled_write_P(PSTR(" ENC "), false); + oled_write(get_enc_mode(), true); + + led_t led_state = host_keyboard_led_state(); + oled_set_cursor(18, 0); + oled_write_P(PSTR("CAP"), led_state.caps_lock); + oled_set_cursor(18, 1); + oled_write_P(PSTR("SCR"), led_state.scroll_lock); + + uint8_t mod_state = get_mods(); + oled_set_cursor(6, 3); + oled_write_P(PSTR("S"), mod_state & MOD_MASK_SHIFT); + oled_advance_char(); + oled_write_P(PSTR("C"), mod_state & MOD_MASK_CTRL); + oled_advance_char(); + oled_write_P(PSTR("A"), mod_state & MOD_MASK_ALT); + oled_advance_char(); + oled_write_P(PSTR("G"), mod_state & MOD_MASK_GUI); + oled_advance_char(); + + oled_write(get_time(), false); + +/* Matrix display is 12 x 12 pixels */ +#define MATRIX_DISPLAY_X 0 +#define MATRIX_DISPLAY_Y 18 + + // matrix + for (uint8_t x = 0; x < MATRIX_ROWS; x++) { + for (uint8_t y = 0; y < MATRIX_COLS; y++) { + bool on = (matrix_get_row(x) & (1 << y)) > 0; + oled_write_pixel(MATRIX_DISPLAY_X + y + 2, MATRIX_DISPLAY_Y + x + 2, on); + } + } + + // outline + draw_line_h(MATRIX_DISPLAY_X, MATRIX_DISPLAY_Y, 19); + draw_line_h(MATRIX_DISPLAY_X, MATRIX_DISPLAY_Y + 9, 19); + draw_line_v(MATRIX_DISPLAY_X, MATRIX_DISPLAY_Y, 9); + draw_line_v(MATRIX_DISPLAY_X + 19, MATRIX_DISPLAY_Y, 9); + + // oled location + draw_line_h(MATRIX_DISPLAY_X + 14, MATRIX_DISPLAY_Y + 2, 3); + + // bodge extra lines for invert layer and enc mode + draw_line_v(35, 0, 8); + draw_line_v(71, 0, 8); +} + +void draw_clock(void) { + oled_set_cursor(0, 0); + oled_write(get_date(), false); + oled_set_cursor(0, 2); + oled_write(get_time(), false); + + oled_set_cursor(12, 0); + oled_write_P(PSTR(" ENC "), false); + oled_write(get_enc_mode(), true); + + oled_set_cursor(13, 1); + oled_write_P(PSTR("LAYER "), false); + oled_write_char(get_highest_layer(layer_state) + 0x30, true); + + led_t led_state = host_keyboard_led_state(); + oled_set_cursor(15, 3); + oled_write_P(PSTR("CAPS"), led_state.caps_lock); + + if (clock_set_mode) { + switch (time_config_idx) { + case 0: // hour + default: + draw_line_h(0, 25, 10); + break; + case 1: // minute + draw_line_h(18, 25, 10); + break; + case 2: // year + draw_line_h(0, 9, 24); + break; + case 3: // month + draw_line_h(30, 9, 10); + break; + case 4: // day + draw_line_h(48, 9, 10); + break; + } + } + + // bodge extra lines for invert layer and enc mode + draw_line_v(101, 0, 8); + draw_line_v(113, 8, 8); +} + +#else + +void oled_request_repaint(void){ +} + +void oled_request_wakeup(void){ +} + +#endif
\ No newline at end of file |