satisfaction_oled.c (7955B)
1 // Copyright 2023 Andrew Kannan 2 // SPDX-License-Identifier: GPL-2.0-or-later 3 4 #include "satisfaction_core.h" 5 #include "action_layer.h" 6 #include "action_util.h" 7 #include "timer.h" 8 #include "matrix.h" 9 #include "led.h" 10 #include "host.h" 11 #include "progmem.h" 12 #include <stdio.h> 13 14 void draw_default(void); 15 void draw_clock(void); 16 17 #ifdef OLED_ENABLE 18 #include "oled_driver.h" 19 20 oled_rotation_t oled_init_kb(oled_rotation_t rotation) { return OLED_ROTATION_0; } 21 22 bool oled_task_kb(void) { 23 if (!oled_task_user()) { return false; } 24 if (!oled_task_needs_to_repaint()) { 25 return false; 26 } 27 oled_clear(); 28 if (clock_set_mode) { 29 draw_clock(); 30 return false; 31 } 32 switch (oled_mode) { 33 default: 34 case OLED_DEFAULT: 35 draw_default(); 36 break; 37 case OLED_TIME: 38 draw_clock(); 39 break; 40 } 41 return false; 42 } 43 44 // Request a repaint of the OLED image without resetting the OLED sleep timer. 45 // Used for things like clock updates that should not keep the OLED turned on 46 // if there is no other activity. 47 void oled_request_repaint(void) { 48 if (is_oled_on()) { 49 oled_repaint_requested = true; 50 } 51 } 52 53 // Request a repaint of the OLED image and reset the OLED sleep timer. 54 // Needs to be called after any activity that should keep the OLED turned on. 55 void oled_request_wakeup(void) { 56 oled_wakeup_requested = true; 57 } 58 59 // Check whether oled_task_user() needs to repaint the OLED image. This 60 // function should be called at the start of oled_task_user(); it also handles 61 // the OLED sleep timer and the OLED_OFF mode. 62 bool oled_task_needs_to_repaint(void) { 63 // In the OLED_OFF mode the OLED is kept turned off; any wakeup requests 64 // are ignored. 65 if ((oled_mode == OLED_OFF) && !clock_set_mode) { 66 oled_wakeup_requested = false; 67 oled_repaint_requested = false; 68 oled_off(); 69 return false; 70 } 71 72 // If OLED wakeup was requested, reset the sleep timer and do a repaint. 73 if (oled_wakeup_requested) { 74 oled_wakeup_requested = false; 75 oled_repaint_requested = false; 76 oled_sleep_timer = timer_read32() + CUSTOM_OLED_TIMEOUT; 77 oled_on(); 78 return true; 79 } 80 81 // If OLED repaint was requested, just do a repaint without touching the 82 // sleep timer. 83 if (oled_repaint_requested) { 84 oled_repaint_requested = false; 85 return true; 86 } 87 88 // If the OLED is currently off, skip the repaint (which would turn the 89 // OLED on if the image is changed in any way). 90 if (!is_oled_on()) { 91 return false; 92 } 93 94 // If the sleep timer has expired while the OLED was on, turn the OLED off. 95 if (timer_expired32(timer_read32(), oled_sleep_timer)) { 96 oled_off(); 97 return false; 98 } 99 100 // Always perform a repaint if the OLED is currently on. (This can 101 // potentially be optimized to avoid unneeded repaints if all possible 102 // state changes are covered by oled_request_repaint() or 103 // oled_request_wakeup(), but then any missed calls to these functions 104 // would result in displaying a stale image.) 105 return true; 106 } 107 108 109 static void draw_line_h(uint8_t x, uint8_t y, uint8_t len) { 110 for (uint8_t i = 0; i < len; i++) { 111 oled_write_pixel(i + x, y, true); 112 } 113 } 114 115 static void draw_line_v(uint8_t x, uint8_t y, uint8_t len) { 116 for (uint8_t i = 0; i < len; i++) { 117 oled_write_pixel(x, i + y, true); 118 } 119 } 120 121 static char* get_enc_mode(void) { 122 switch (encoder_mode) { 123 default: 124 case ENC_MODE_VOLUME: 125 return "VOL"; 126 case ENC_MODE_MEDIA: 127 return "MED"; 128 case ENC_MODE_SCROLL: 129 return "SCR"; 130 case ENC_MODE_BRIGHTNESS: 131 return "BRT"; 132 case ENC_MODE_BACKLIGHT: 133 return "BKL"; 134 case ENC_MODE_CLOCK_SET: 135 return "CLK"; 136 case ENC_MODE_CUSTOM0: 137 return "CS0"; 138 case ENC_MODE_CUSTOM1: 139 return "CS1"; 140 case ENC_MODE_CUSTOM2: 141 return "CS2"; 142 } 143 } 144 145 static char* get_time(void) { 146 uint8_t hour = last_minute / 60; 147 uint16_t minute = last_minute % 60; 148 149 if (encoder_mode == ENC_MODE_CLOCK_SET) { 150 hour = hour_config; 151 minute = minute_config; 152 } 153 154 bool is_pm = (hour / 12) > 0; 155 hour = hour % 12; 156 if (hour == 0) { 157 hour = 12; 158 } 159 160 static char time_str[8] = ""; 161 snprintf(time_str, sizeof(time_str), "%02hhu:%02hu%s", hour, minute, is_pm ? "pm" : "am"); 162 163 return time_str; 164 } 165 166 static char* get_date(void) { 167 int16_t year = last_timespec.year + 1980; 168 int8_t month = last_timespec.month; 169 int8_t day = last_timespec.day; 170 171 if (encoder_mode == ENC_MODE_CLOCK_SET) { 172 year = year_config + 1980; 173 month = month_config; 174 day = day_config; 175 } 176 177 static char date_str[11] = ""; 178 snprintf(date_str, sizeof(date_str), "%04hd-%02hhd-%02hhd", year, month, day); 179 180 return date_str; 181 } 182 183 void draw_default(void) { 184 oled_write_P(PSTR("LAYER "), false); 185 oled_write_char(get_highest_layer(layer_state) + 0x30, true); 186 187 oled_write_P(PSTR(" ENC "), false); 188 oled_write(get_enc_mode(), true); 189 190 led_t led_state = host_keyboard_led_state(); 191 oled_set_cursor(18, 0); 192 oled_write_P(PSTR("CAP"), led_state.caps_lock); 193 oled_set_cursor(18, 1); 194 oled_write_P(PSTR("SCR"), led_state.scroll_lock); 195 196 uint8_t mod_state = get_mods(); 197 oled_set_cursor(6, 3); 198 oled_write_P(PSTR("S"), mod_state & MOD_MASK_SHIFT); 199 oled_advance_char(); 200 oled_write_P(PSTR("C"), mod_state & MOD_MASK_CTRL); 201 oled_advance_char(); 202 oled_write_P(PSTR("A"), mod_state & MOD_MASK_ALT); 203 oled_advance_char(); 204 oled_write_P(PSTR("G"), mod_state & MOD_MASK_GUI); 205 oled_advance_char(); 206 207 oled_write(get_time(), false); 208 209 /* Matrix display is 12 x 12 pixels */ 210 #define MATRIX_DISPLAY_X 0 211 #define MATRIX_DISPLAY_Y 18 212 213 // matrix 214 for (uint8_t x = 0; x < MATRIX_ROWS; x++) { 215 for (uint8_t y = 0; y < MATRIX_COLS; y++) { 216 bool on = (matrix_get_row(x) & (1 << y)) > 0; 217 oled_write_pixel(MATRIX_DISPLAY_X + y + 2, MATRIX_DISPLAY_Y + x + 2, on); 218 } 219 } 220 221 // outline 222 draw_line_h(MATRIX_DISPLAY_X, MATRIX_DISPLAY_Y, 19); 223 draw_line_h(MATRIX_DISPLAY_X, MATRIX_DISPLAY_Y + 9, 19); 224 draw_line_v(MATRIX_DISPLAY_X, MATRIX_DISPLAY_Y, 9); 225 draw_line_v(MATRIX_DISPLAY_X + 19, MATRIX_DISPLAY_Y, 9); 226 227 // oled location 228 draw_line_h(MATRIX_DISPLAY_X + 14, MATRIX_DISPLAY_Y + 2, 3); 229 230 // bodge extra lines for invert layer and enc mode 231 draw_line_v(35, 0, 8); 232 draw_line_v(71, 0, 8); 233 } 234 235 void draw_clock(void) { 236 oled_set_cursor(0, 0); 237 oled_write(get_date(), false); 238 oled_set_cursor(0, 2); 239 oled_write(get_time(), false); 240 241 oled_set_cursor(12, 0); 242 oled_write_P(PSTR(" ENC "), false); 243 oled_write(get_enc_mode(), true); 244 245 oled_set_cursor(13, 1); 246 oled_write_P(PSTR("LAYER "), false); 247 oled_write_char(get_highest_layer(layer_state) + 0x30, true); 248 249 led_t led_state = host_keyboard_led_state(); 250 oled_set_cursor(15, 3); 251 oled_write_P(PSTR("CAPS"), led_state.caps_lock); 252 253 if (clock_set_mode) { 254 switch (time_config_idx) { 255 case 0: // hour 256 default: 257 draw_line_h(0, 25, 10); 258 break; 259 case 1: // minute 260 draw_line_h(18, 25, 10); 261 break; 262 case 2: // year 263 draw_line_h(0, 9, 24); 264 break; 265 case 3: // month 266 draw_line_h(30, 9, 10); 267 break; 268 case 4: // day 269 draw_line_h(48, 9, 10); 270 break; 271 } 272 } 273 274 // bodge extra lines for invert layer and enc mode 275 draw_line_v(101, 0, 8); 276 draw_line_v(113, 8, 8); 277 } 278 279 #else 280 281 void oled_request_repaint(void){ 282 } 283 284 void oled_request_wakeup(void){ 285 } 286 287 #endif