#pragma once #include #include class DisplayDriver { int _w, _h; protected: DisplayDriver(int w, int h) { _w = w; _h = h; } public: enum Color { DARK=0, LIGHT, RED, GREEN, BLUE, YELLOW, ORANGE }; // on b/w screen, colors will be !=0 synonym of light int width() const { return _w; } int height() const { return _h; } virtual bool isOn() = 0; virtual void turnOn() = 0; virtual void turnOff() = 0; virtual void clear() = 0; virtual void startFrame(Color bkg = DARK) = 0; virtual void setTextSize(int sz) = 0; virtual void setColor(Color c) = 0; virtual void setCursor(int x, int y) = 0; virtual void print(const char* str) = 0; virtual void printWordWrap(const char* str, int max_width) { print(str); } // fallback to basic print() if no override virtual void fillRect(int x, int y, int w, int h) = 0; virtual void drawRect(int x, int y, int w, int h) = 0; virtual void drawXbm(int x, int y, const uint8_t* bits, int w, int h) = 0; virtual uint16_t getTextWidth(const char* str) = 0; virtual void drawTextCentered(int mid_x, int y, const char* str) { // helper method (override to optimise) int w = getTextWidth(str); setCursor(mid_x - w/2, y); print(str); } virtual void drawTextRightAlign(int x_anch, int y, const char* str) { int w = getTextWidth(str); setCursor(x_anch - w, y); print(str); } virtual void drawTextLeftAlign(int x_anch, int y, const char* str) { setCursor(x_anch, y); print(str); } // convert UTF-8 characters to displayable block characters for compatibility virtual void translateUTF8ToBlocks(char* dest, const char* src, size_t dest_size) { size_t j = 0; #ifdef OLED_RU char last_char = 0; char cc = 0; #endif for (size_t i = 0; src[i] != 0 && j < dest_size - 1; i++) { unsigned char c = (unsigned char)src[i]; if (c >= 32 && c <= 126) { #ifdef OLED_RU last_char = 0; dest[j++] = c; // ASCII printable } else if (last_char == 0 && (c == 0xD0 || c == 0xD1 || c == 0xD2)) { // Cyrillic UTF-8 start byte for 2 bytes sequence last_char = c; } else if (last_char > 0) { // Cyrillic UTF-8 next byte cc = 0; // Translated charcode switch (last_char) { // map UTF-8 cyrillic chars to it CP-1251 ASCII codes case 0xD0: { if (c == 132) cc = (170); // Є if (c == 134) cc = (178); // І if (c == 135) cc = (175); // Ї if (c == 129) cc = (168); // Ё if (c > 143 && c < 192) cc = (c + 48); break; } case 0xD1: { if (c == 148) cc = (186); // є if (c == 150) cc = (179); // і if (c == 151) cc = (191); // ї if (c == 145) cc = (184); // ё if (c > 127 && c < 144) cc = (c + 112); break; } case 0xD2: { if (c == 144) cc = (165); // Ґ if (c == 145) cc = (180); // ґ break; } } if (cc > 0) { dest[j++] = cc; } last_char = 0; } else if (c >= 0x80) { last_char = 0; dest[j++] = '\xAE'; // CP1251 smile emoji replace #else dest[j++] = c; // ASCII printable } else if (c >= 0x80) { dest[j++] = '\xDB'; // CP437 full block █ #endif while (src[i+1] && (src[i+1] & 0xC0) == 0x80) i++; // skip UTF-8 continuation bytes } } dest[j] = 0; } // draw text with ellipsis if it exceeds max_width virtual void drawTextEllipsized(int x, int y, int max_width, const char* str) { char temp_str[256]; // reasonable buffer size size_t len = strlen(str); if (len >= sizeof(temp_str)) len = sizeof(temp_str) - 1; memcpy(temp_str, str, len); temp_str[len] = 0; if (getTextWidth(temp_str) <= max_width) { setCursor(x, y); print(temp_str); return; } // for variable-width fonts (GxEPD), add space after ellipsis // for fixed-width fonts (OLED), keep tight spacing to save precious characters const char* ellipsis; // use a simple heuristic: if 'i' and 'l' have different widths, it's variable-width int i_width = getTextWidth("i"); int l_width = getTextWidth("l"); if (i_width != l_width) { ellipsis = "... "; // variable-width fonts: add space } else { ellipsis = "..."; // fixed-width fonts: no space } int ellipsis_width = getTextWidth(ellipsis); int str_len = strlen(temp_str); while (str_len > 0 && getTextWidth(temp_str) > max_width - ellipsis_width) { temp_str[--str_len] = 0; } strcat(temp_str, ellipsis); setCursor(x, y); print(temp_str); } virtual void endFrame() = 0; };