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 }