qmk_firmware

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

display_oled.c (14031B)


      1 // Copyright 2025 Dasky (@daskygit)
      2 // SPDX-License-Identifier: GPL-2.0-or-later
      3 
      4 #include "quantum.h"
      5 
      6 static uint16_t current_keycode = 0xFF;
      7 
      8 static const char *depad_str(const char *depad_str, char depad_char) {
      9     while (*depad_str == depad_char)
     10         ++depad_str;
     11     return depad_str;
     12 }
     13 
     14 static void render_spacer(uint8_t char_length) {
     15     static const char PROGMEM spacer_char[] = {8, 8, 8, 8, 8, 8, 8};
     16     if (char_length > 5) {
     17         char_length = 5;
     18     }
     19     for (uint8_t i = 0; i < char_length; i++) {
     20         oled_write_raw_P(spacer_char, sizeof(spacer_char));
     21         oled_advance_char();
     22     }
     23 }
     24 
     25 __attribute__((weak)) void render_logo(void) {
     26     static const char PROGMEM mb_logo[] = {
     27         252, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 31, 31, 31, 31,  31,  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   227, 227, 227, 3,   227, 227, 227, 3,   227, 227, 227, 3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   7,   254, 252, 255, 255, 255, 255, 255, 7,   7,   7,   7,   7,   255, 7,   7,   7,   7,   7,   255, 7,   7,   7,   7,   7,   255, 7,   7,   7,   7,   7,   255, 7,   7,   7,   7,   7,   255, 255, 255, 4,   4,   4,   4,   4,  255,
     28         7,   7,   7,   7,   7,   255, 7,   7,   7,   7,   7,   255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  0,  0,  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   224, 224, 224, 0,   238, 238, 238, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   255, 255, 255, 255, 255, 255, 255, 65,  65,  65,  65,  65,  255, 255, 255, 255, 255, 255, 255, 65,  65,  65,  65,  65,  255, 255, 255, 255, 255, 255, 255, 65,  65,  65,  65,  65,  255, 255, 255, 65,  65,  65,  65,  65,  255, 127, 127, 127, 127, 127, 255, 65,  65,  65,  65,  65,  255, 255, 255, 255, 255, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  0,
     29         0,   0,   0,   0,   0,   0,   0,   0,   0,   14,  14,  14,  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  0,  0,  255, 255, 63,  127, 255, 255, 255, 240, 240, 240, 240, 240, 255, 255, 255, 255, 255, 255, 255, 240, 240, 240, 240, 240, 255, 255, 255, 255, 255, 255, 255, 240, 240, 240, 240, 240, 255, 255, 255, 240, 240, 240, 240, 240, 255, 240, 240, 240, 240, 240, 255, 240, 240, 240, 240, 240, 255, 255, 255, 255, 255, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 199, 199, 199, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 224, 127, 63,
     30     };
     31     oled_write_raw_P(mb_logo, sizeof(mb_logo));
     32 }
     33 
     34 void render_small_mb_logo(void) {
     35     static const char PROGMEM small_mb_logo[] = {
     36         112, 112, 112, 0, 112, 112, 112, 0, 112, 112, 112, 0, 112, 112, 112, 0, 112, 112, 112, 0, 0, 119, 119, 119, 0, 112, 112, 112, 0, 112, 112, 112, 119, 119, 119, 0, 0, 0, 0, 0, 119, 119, 119, 0, 0, 0, 0, 0, 119, 119, 119, 0, 0, 119, 119, 119, 0, 112, 112, 112, 0, 119, 119, 119,
     37     };
     38     oled_write_raw_P(small_mb_logo, sizeof(small_mb_logo));
     39 }
     40 
     41 static uint8_t last_hue;
     42 static uint8_t last_sat;
     43 static uint8_t last_val;
     44 static uint8_t last_mode;
     45 
     46 void render_rgb_info(void) {
     47     last_hue  = rgb_matrix_get_hue();
     48     last_sat  = rgb_matrix_get_sat();
     49     last_val  = rgb_matrix_get_val();
     50     last_mode = rgb_matrix_get_mode();
     51     oled_set_cursor(0, 6);
     52     oled_write("H:", false);
     53     oled_write(depad_str(get_u16_str(last_hue, ' '), ' '), false);
     54     oled_set_cursor(0, 7);
     55     oled_write("S:", false);
     56     oled_write_ln(depad_str(get_u16_str(last_sat, ' '), ' '), false);
     57     oled_set_cursor(0, 8);
     58     oled_write("V:", false);
     59     oled_write_ln(depad_str(get_u16_str(last_val, ' '), ' '), false);
     60     oled_set_cursor(0, 9);
     61     oled_write("M:", false);
     62     oled_write_ln(depad_str(get_u16_str(last_mode, ' '), ' '), false);
     63 }
     64 
     65 oled_rotation_t oled_init_kb(oled_rotation_t rotation) {
     66     if (is_keyboard_master()) {
     67         return OLED_ROTATION_270;
     68     }
     69     return rotation;
     70 }
     71 
     72 char basic_codes_to_name[57] = {' ', ' ', ' ', ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'R', 'E', 'B', 'T', '_', '-', '=', '[', ']', '\\', '#', ';', '\'', '`', ',', '.', '/'};
     73 
     74 const char *keycode_string(uint16_t keycode) {
     75     char       *keycode_str;
     76     static char key;
     77     switch (keycode) {
     78         case 0 ... 56:
     79             key = pgm_read_byte(&basic_codes_to_name[keycode]);
     80             return &key;
     81         case KC_CAPS:
     82             keycode_str = "Caps\0";
     83             break;
     84         case KC_SCRL:
     85             keycode_str = "Scrl\0";
     86             break;
     87         case KC_PAUS:
     88             keycode_str = "Pause\0";
     89             break;
     90         case KC_DEL:
     91             keycode_str = "Del\0";
     92             break;
     93         case KC_NUM:
     94             keycode_str = "Num\0";
     95             break;
     96         case KC_MUTE:
     97             keycode_str = "Mute\0";
     98             break;
     99         case KC_VOLU:
    100             keycode_str = "VolUp\0";
    101             break;
    102         case KC_VOLD:
    103             keycode_str = "VolD\0";
    104             break;
    105         case KC_MNXT:
    106             keycode_str = "Next\0";
    107             break;
    108         case KC_MPRV:
    109             keycode_str = "Prev\0";
    110             break;
    111         case KC_MSTP:
    112             keycode_str = "Stop\0";
    113             break;
    114         case KC_MPLY:
    115             keycode_str = "Play\0";
    116             break;
    117         case QK_MODS ... QK_MODS_MAX:
    118             keycode_str = "MOD()\0";
    119             break;
    120         case QK_MOD_TAP ... QK_MOD_TAP_MAX:
    121             keycode_str = "MT()\0";
    122             break;
    123         case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
    124             keycode_str = "LT()\0";
    125             break;
    126         case QK_LAYER_MOD ... QK_LAYER_MOD_MAX:
    127             keycode_str = "LM()\0";
    128             break;
    129         case QK_TO ... QK_TO_MAX:
    130             keycode_str = "TO()\0";
    131             break;
    132         case QK_MOMENTARY ... QK_MOMENTARY_MAX:
    133             keycode_str = "MO()\0";
    134             break;
    135         case QK_DEF_LAYER ... QK_DEF_LAYER_MAX:
    136             keycode_str = "DF()\0";
    137             break;
    138         case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX:
    139             keycode_str = "TG()\0";
    140             break;
    141         case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_MOD_MAX:
    142             keycode_str = "1SHOT\0";
    143             break;
    144         case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX:
    145             keycode_str = "TT()\0";
    146             break;
    147         case QK_PERSISTENT_DEF_LAYER ... QK_PERSISTENT_DEF_LAYER_MAX:
    148             keycode_str = "PDF()\0";
    149             break;
    150         case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX:
    151             keycode_str = "SWAP\0";
    152             break;
    153         case QK_TAP_DANCE ... QK_TAP_DANCE_MAX:
    154             keycode_str = "TD()\0";
    155             break;
    156         case QK_MAGIC ... QK_MAGIC_MAX:
    157             keycode_str = "Magic\0";
    158             break;
    159         case QK_MIDI ... QK_MIDI_MAX:
    160             keycode_str = "Midi\0";
    161             break;
    162         case QK_SEQUENCER ... QK_SEQUENCER_MAX:
    163             keycode_str = "Seq\0";
    164             break;
    165         case QK_JOYSTICK ... QK_JOYSTICK_MAX:
    166             keycode_str = "Joy\0";
    167             break;
    168         case QK_PROGRAMMABLE_BUTTON ... QK_PROGRAMMABLE_BUTTON_MAX:
    169             keycode_str = "Prog\0";
    170             break;
    171         case QK_AUDIO ... QK_AUDIO_MAX:
    172             keycode_str = "Audio\0";
    173             break;
    174         case QK_STENO ... QK_STENO_MAX:
    175             keycode_str = "Steno\0";
    176             break;
    177         case QK_MACRO ... QK_MACRO_MAX:
    178             keycode_str = "Macro\0";
    179             break;
    180         case QK_CONNECTION ... QK_CONNECTION_MAX:
    181             keycode_str = "Conn\0";
    182             break;
    183         case QK_LIGHTING ... QK_LIGHTING_MAX:
    184             keycode_str = "Light\0";
    185             break;
    186         case QK_QUANTUM ... QK_QUANTUM_MAX:
    187             keycode_str = "Quant\0";
    188             break;
    189         case QK_KB ... QK_KB_MAX:
    190             keycode_str = "KB\0";
    191             break;
    192         case QK_USER ... QK_USER_MAX:
    193             keycode_str = "USER\0";
    194             break;
    195         case QK_UNICODEMAP ... QK_UNICODEMAP_PAIR_MAX:
    196             keycode_str = "Uni\0";
    197             break;
    198         default:
    199             keycode_str = "Undef\0";
    200             break;
    201     }
    202 
    203     return keycode_str;
    204 }
    205 
    206 const char *layer_string(uint32_t layer) {
    207     char *layer_str;
    208     switch (layer) {
    209         case 0:
    210             layer_str = "Zero\0";
    211             break;
    212         case 1:
    213             layer_str = "One\0";
    214             break;
    215         case 2:
    216             layer_str = "Two\0";
    217             break;
    218         case 3:
    219             layer_str = "Three\0";
    220             break;
    221         case 4:
    222             layer_str = "Four\0";
    223             break;
    224         case 5:
    225             layer_str = "Five\0";
    226             break;
    227         case 6:
    228             layer_str = "Six\0";
    229             break;
    230         case 7:
    231             layer_str = "Seven\0";
    232             break;
    233         default:
    234             return get_u16_str(layer, ' ');
    235     }
    236 
    237     return layer_str;
    238 }
    239 
    240 bool process_detected_host_os_kb(os_variant_t detected_os) {
    241     if (!process_detected_host_os_user(detected_os)) {
    242         return false;
    243     }
    244 
    245     oled_set_cursor(0, 10);
    246     switch (detected_os) {
    247         case OS_MACOS:
    248             oled_write_ln("MacOS", false);
    249         case OS_IOS:
    250             oled_write_ln("Apple", false);
    251             break;
    252         case OS_WINDOWS:
    253             oled_write_ln("Win", false);
    254             break;
    255         case OS_LINUX:
    256             oled_write_ln("Linux", false);
    257             break;
    258         case OS_UNSURE:
    259             oled_write_ln("Unkno", false);
    260 
    261             break;
    262     }
    263 
    264     return true;
    265 }
    266 
    267 void keyboard_post_init_kb(void) {
    268     if (!is_keyboard_master()) {
    269         render_logo();
    270     } else {
    271         oled_set_cursor(0, 0);
    272         oled_write("Layer", false);
    273         render_spacer(5);
    274         oled_write_ln(layer_string(get_highest_layer(layer_state)), false);
    275 
    276         oled_set_cursor(0, 4);
    277         oled_write_ln("Key", false);
    278         render_spacer(3);
    279         oled_advance_page(false);
    280         oled_write_ln("None", false);
    281 
    282         oled_set_cursor(0, 8);
    283         oled_write_ln("OS", false);
    284         render_spacer(2);
    285         oled_advance_page(false);
    286         oled_write_ln("Wait", false);
    287 
    288         oled_set_cursor(0, 12);
    289         oled_write_ln("Rate", false);
    290         render_spacer(4);
    291     }
    292     keyboard_post_init_user();
    293 }
    294 
    295 layer_state_t layer_state_set_kb(layer_state_t state) {
    296     state = layer_state_set_user(state);
    297     oled_set_cursor(0, 2);
    298     oled_write_ln(layer_string(get_highest_layer(state)), false);
    299     return state;
    300 }
    301 
    302 bool process_record_kb(uint16_t keycode, keyrecord_t *record) {
    303     current_keycode = keycode;
    304     return process_record_user(keycode, record);
    305 };
    306 
    307 uint16_t loop_rate = 0;
    308 void     housekeeping_task_kb(void) {
    309     if (is_keyboard_master()) {
    310         static uint32_t     loop_count = 0;
    311         static fast_timer_t loop_time  = 0;
    312         loop_count++;
    313         if (timer_elapsed_fast(loop_time) > 1000) {
    314             loop_time  = timer_read_fast();
    315             loop_rate  = loop_count > UINT16_MAX ? UINT16_MAX : loop_count;
    316             loop_count = 0;
    317             if (is_oled_on()) {
    318                 oled_set_cursor(0, 14);
    319                 oled_write(depad_str(get_u16_str(loop_rate, ' '), ' '), false);
    320             }
    321         }
    322     }
    323     if (is_oled_on() && last_input_activity_elapsed() > OLED_TIMEOUT) {
    324         oled_off();
    325     }
    326 }
    327 
    328 void oled_reinit_slave(void) {
    329     oled_init(OLED_ROTATION_270);
    330     oled_clear();
    331     oled_set_cursor(0, 0);
    332     oled_write_ln("WPM", false);
    333     render_spacer(3);
    334     oled_advance_page(false);
    335     oled_write_ln(depad_str(get_u16_str(get_current_wpm(), ' '), ' '), false);
    336 
    337     oled_set_cursor(0, 4);
    338     oled_write_ln("RGB", false);
    339     render_spacer(3);
    340     render_rgb_info();
    341 
    342     oled_set_cursor(0, 13);
    343     render_small_mb_logo();
    344 }
    345 
    346 bool oled_task_kb(void) {
    347     if (!oled_task_user()) {
    348         return false;
    349     }
    350 
    351     static uint16_t last_keycode         = 0xFF;
    352     static bool     oled_slave_init_done = false;
    353 
    354     if (is_keyboard_master()) {
    355         if (last_keycode != current_keycode) {
    356             oled_set_cursor(0, 6);
    357             if (current_keycode < ARRAY_SIZE(basic_codes_to_name)) {
    358                 oled_write_char(basic_codes_to_name[current_keycode], false);
    359                 oled_advance_page(true);
    360             } else {
    361                 oled_write_ln(keycode_string(current_keycode), false);
    362             }
    363             last_keycode = current_keycode;
    364         }
    365     } else {
    366         if (!oled_slave_init_done) {
    367             if (timer_elapsed32(0) > 5000) {
    368                 oled_slave_init_done = true;
    369                 oled_reinit_slave();
    370             }
    371         } else {
    372             static uint16_t last_wpm = 0;
    373             if (rgb_matrix_get_hue() != last_hue || rgb_matrix_get_sat() != last_sat || rgb_matrix_get_val() != last_val || rgb_matrix_get_mode() != last_mode) {
    374                 render_rgb_info();
    375             }
    376             if (last_wpm != get_current_wpm()) {
    377                 last_wpm = get_current_wpm();
    378                 oled_set_cursor(0, 2);
    379                 oled_write_ln(depad_str(get_u16_str(last_wpm, ' '), ' '), false);
    380             }
    381         }
    382     }
    383 
    384     return false;
    385 }