[2.0.x] Buffer overflow and scroll fix, UTF8 cleanup (#10844)
This commit is contained in:
		
				
					committed by
					
						 Scott Lahteine
						Scott Lahteine
					
				
			
			
				
	
			
			
			
						parent
						
							235facd545
						
					
				
				
					commit
					6f330f397e
				
			| @@ -162,22 +162,15 @@ void spiBegin (void) { | ||||
|     // away. When clock is not known, use a loop instead, which generates | ||||
|     // shorter code. | ||||
|     if (__builtin_constant_p(spiClock)) { | ||||
|       if (spiClock >= F_CPU / 2) { | ||||
|         clockDiv = 0; | ||||
|       } else if (spiClock >= F_CPU / 4) { | ||||
|         clockDiv = 1; | ||||
|       } else if (spiClock >= F_CPU / 8) { | ||||
|         clockDiv = 2; | ||||
|       } else if (spiClock >= F_CPU / 16) { | ||||
|         clockDiv = 3; | ||||
|       } else if (spiClock >= F_CPU / 32) { | ||||
|         clockDiv = 4; | ||||
|       } else if (spiClock >= F_CPU / 64) { | ||||
|         clockDiv = 5; | ||||
|       } else { | ||||
|         clockDiv = 6; | ||||
|       } | ||||
|     } else { | ||||
|       if (spiClock >= F_CPU / 2)       clockDiv = 0; | ||||
|       else if (spiClock >= F_CPU / 4)  clockDiv = 1; | ||||
|       else if (spiClock >= F_CPU / 8)  clockDiv = 2; | ||||
|       else if (spiClock >= F_CPU / 16) clockDiv = 3; | ||||
|       else if (spiClock >= F_CPU / 32) clockDiv = 4; | ||||
|       else if (spiClock >= F_CPU / 64) clockDiv = 5; | ||||
|       else                             clockDiv = 6; | ||||
|     } | ||||
|     else { | ||||
|       uint32_t clockSetting = F_CPU / 2; | ||||
|       clockDiv = 0; | ||||
|       while (clockDiv < 6 && spiClock < clockSetting) { | ||||
| @@ -187,8 +180,7 @@ void spiBegin (void) { | ||||
|     } | ||||
|  | ||||
|     // Compensate for the duplicate fosc/64 | ||||
|     if (clockDiv == 6) | ||||
|       clockDiv = 7; | ||||
|     if (clockDiv == 6) clockDiv = 7; | ||||
|  | ||||
|     // Invert the SPI2X bit | ||||
|     clockDiv ^= 0x1; | ||||
|   | ||||
| @@ -56,7 +56,6 @@ extern uint8_t ubl_cnt; | ||||
| /////////////////////////////////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
| #if ENABLED(ULTRA_LCD) | ||||
|   extern char lcd_status_message[]; | ||||
|   void lcd_quick_feedback(const bool clear_buttons); | ||||
| #endif | ||||
|  | ||||
|   | ||||
| @@ -135,10 +135,6 @@ | ||||
|  | ||||
| // External references | ||||
|  | ||||
| #if ENABLED(ULTRA_LCD) | ||||
|   extern char lcd_status_message[]; | ||||
| #endif | ||||
|  | ||||
| // Private functions | ||||
|  | ||||
| static uint16_t circle_flags[16], horizontal_mesh_line_flags[16], vertical_mesh_line_flags[16]; | ||||
| @@ -508,8 +504,6 @@ inline bool prime_nozzle() { | ||||
|  | ||||
|       wait_for_release(); | ||||
|  | ||||
|       strcpy_P(lcd_status_message, PSTR("Done Priming")); // Hack to get the message up. May be obsolete. | ||||
|  | ||||
|       lcd_setstatusPGM(PSTR("Done Priming"), 99); | ||||
|       lcd_quick_feedback(true); | ||||
|       lcd_external_control = false; | ||||
|   | ||||
| @@ -519,15 +519,12 @@ void GcodeSuite::G33() { | ||||
|   } | ||||
|  | ||||
|   // Report settings | ||||
|  | ||||
|   const char *checkingac = PSTR("Checking... AC"); | ||||
|   const char* checkingac = PSTR("Checking... AC"); | ||||
|   serialprintPGM(checkingac); | ||||
|   if (verbose_level == 0) SERIAL_PROTOCOLPGM(" (DRY-RUN)"); | ||||
|   if (set_up) SERIAL_PROTOCOLPGM("  (SET-UP)"); | ||||
|   SERIAL_EOL(); | ||||
|   char mess[11]; | ||||
|   strcpy_P(mess, checkingac); | ||||
|   lcd_setstatus(mess); | ||||
|   lcd_setstatusPGM(checkingac); | ||||
|  | ||||
|   print_calibration_settings(_endstop_results, _angle_results); | ||||
|  | ||||
|   | ||||
| @@ -33,7 +33,7 @@ FORCE_INLINE void _draw_centered_temp(const int16_t temp, const uint8_t x, const | ||||
|   const char * const str = itostr3(temp); | ||||
|   lcd_moveto(x - (str[0] != ' ' ? 0 : str[1] != ' ' ? 1 : 2) * DOG_CHAR_WIDTH / 2, y); | ||||
|   lcd_put_u8str(str); | ||||
|   lcd_put_u8str_rom(PSTR(LCD_STR_DEGREE " ")); | ||||
|   lcd_put_u8str_P(PSTR(LCD_STR_DEGREE " ")); | ||||
| } | ||||
|  | ||||
| #ifndef HEAT_INDICATOR_X | ||||
| @@ -113,7 +113,7 @@ FORCE_INLINE void _draw_axis_value(const AxisEnum axis, const char *value, const | ||||
|     else { | ||||
|       #if DISABLED(HOME_AFTER_DEACTIVATE) && DISABLED(DISABLE_REDUCED_ACCURACY_WARNING) | ||||
|         if (!axis_known_position[axis]) | ||||
|           lcd_put_u8str_rom(axis == Z_AXIS ? PSTR("      ") : PSTR("    ")); | ||||
|           lcd_put_u8str_P(axis == Z_AXIS ? PSTR("      ") : PSTR("    ")); | ||||
|         else | ||||
|       #endif | ||||
|           lcd_put_u8str(value); | ||||
| @@ -124,36 +124,77 @@ FORCE_INLINE void _draw_axis_value(const AxisEnum axis, const char *value, const | ||||
| inline void lcd_implementation_status_message(const bool blink) { | ||||
|   #if ENABLED(STATUS_MESSAGE_SCROLLING) | ||||
|     static bool last_blink = false; | ||||
|     const uint8_t slen = utf8_strlen(lcd_status_message); | ||||
|     const char *stat = lcd_status_message + status_scroll_pos; | ||||
|     if (slen <= LCD_WIDTH) | ||||
|       lcd_put_u8str(stat);                                      // The string isn't scrolling | ||||
|  | ||||
|     // Get the UTF8 character count of the string | ||||
|     uint8_t slen = utf8_strlen(lcd_status_message); | ||||
|  | ||||
|     // If the string fits into the LCD, just print it and do not scroll it | ||||
|     if (slen <= LCD_WIDTH) { | ||||
|  | ||||
|       // The string isn't scrolling and may not fill the screen | ||||
|       lcd_put_u8str(lcd_status_message); | ||||
|  | ||||
|       // Fill the rest with spaces | ||||
|       while (slen < LCD_WIDTH) { | ||||
|         lcd_put_wchar(' '); | ||||
|         ++slen; | ||||
|       } | ||||
|     } | ||||
|     else { | ||||
|       if (status_scroll_pos <= slen - LCD_WIDTH) | ||||
|         lcd_put_u8str(stat);                                    // The string fills the screen | ||||
|       // String is larger than the available space in screen. | ||||
|  | ||||
|       // Get a pointer to the next valid UTF8 character | ||||
|       const char *stat = lcd_status_message + status_scroll_offset; | ||||
|  | ||||
|       // Get the string remaining length | ||||
|       const uint8_t rlen = utf8_strlen(stat); | ||||
|  | ||||
|       // If we have enough characters to display | ||||
|       if (rlen >= LCD_WIDTH) { | ||||
|         // The remaining string fills the screen - Print it | ||||
|         lcd_put_u8str_max(stat, LCD_PIXEL_WIDTH); | ||||
|       } | ||||
|       else { | ||||
|         uint8_t chars = LCD_WIDTH; | ||||
|         if (status_scroll_pos < slen) {                         // First string still visible | ||||
|           lcd_put_u8str(stat);                                  // The string leaves space | ||||
|           chars -= slen - status_scroll_pos;                    // Amount of space left | ||||
|         } | ||||
|         lcd_put_wchar('.');                                         // Always at 1+ spaces left, draw a dot | ||||
|         if (--chars) { | ||||
|           if (status_scroll_pos < slen + 1)                     // Draw a second dot if there's space | ||||
|             --chars, lcd_put_wchar('.'); | ||||
|           if (chars) lcd_put_u8str_max(lcd_status_message, chars);  // Print a second copy of the message | ||||
|         // The remaining string does not completely fill the screen | ||||
|         lcd_put_u8str_max(stat, LCD_PIXEL_WIDTH);         // The string leaves space | ||||
|         uint8_t chars = LCD_WIDTH - rlen;                 // Amount of space left in characters | ||||
|  | ||||
|         lcd_put_wchar('.');                               // Always at 1+ spaces left, draw a dot | ||||
|         if (--chars) {                                    // Draw a second dot if there's space | ||||
|           lcd_put_wchar('.'); | ||||
|           if (--chars) { | ||||
|             // Print a second copy of the message | ||||
|             lcd_put_u8str_max(lcd_status_message, LCD_PIXEL_WIDTH - ((rlen+2) * DOG_CHAR_WIDTH));  | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|       if (last_blink != blink) { | ||||
|         last_blink = blink; | ||||
|         // Skip any non-printing bytes | ||||
|         if (status_scroll_pos < slen) while (!PRINTABLE(lcd_status_message[status_scroll_pos])) status_scroll_pos++; | ||||
|         if (++status_scroll_pos >= slen + 2) status_scroll_pos = 0; | ||||
|  | ||||
|         // Adjust by complete UTF8 characters | ||||
|         if (status_scroll_offset < slen) { | ||||
|           status_scroll_offset++; | ||||
|           while (!START_OF_UTF8_CHAR(lcd_status_message[status_scroll_offset])) | ||||
|             status_scroll_offset++; | ||||
|         } | ||||
|         else | ||||
|           status_scroll_offset = 0; | ||||
|       } | ||||
|     } | ||||
|   #else | ||||
|     UNUSED(blink); | ||||
|     lcd_put_u8str(lcd_status_message); | ||||
|  | ||||
|     // Get the UTF8 character count of the string | ||||
|     uint8_t slen = utf8_strlen(lcd_status_message); | ||||
|  | ||||
|     // Just print the string to the LCD | ||||
|     lcd_put_u8str_max(lcd_status_message, LCD_PIXEL_WIDTH); | ||||
|  | ||||
|     // Fill the rest with spaces if there are missing spaces | ||||
|     while (slen < LCD_WIDTH) { | ||||
|       lcd_put_wchar(' '); | ||||
|       ++slen; | ||||
|     } | ||||
|   #endif | ||||
| } | ||||
|  | ||||
| @@ -417,7 +458,7 @@ static void lcd_implementation_status_screen() { | ||||
|       lcd_put_wchar('%'); | ||||
|       lcd_setFont(FONT_MENU); | ||||
|       lcd_moveto(47, 50); | ||||
|       lcd_put_wchar(LCD_STR_FILAM_DIA[0]); // lcd_put_u8str_rom(PSTR(LCD_STR_FILAM_DIA)); | ||||
|       lcd_put_wchar(LCD_STR_FILAM_DIA[0]); // lcd_put_u8str_P(PSTR(LCD_STR_FILAM_DIA)); | ||||
|       lcd_moveto(93, 50); | ||||
|       lcd_put_wchar(LCD_STR_FILAM_MUL[0]); | ||||
|     #endif | ||||
| @@ -437,10 +478,10 @@ static void lcd_implementation_status_screen() { | ||||
|         lcd_implementation_status_message(blink); | ||||
|       } | ||||
|       else { | ||||
|         lcd_put_u8str_rom(PSTR(LCD_STR_FILAM_DIA)); | ||||
|         lcd_put_u8str_P(PSTR(LCD_STR_FILAM_DIA)); | ||||
|         lcd_put_wchar(':'); | ||||
|         lcd_put_u8str(wstring); | ||||
|         lcd_put_u8str_rom(PSTR("  " LCD_STR_FILAM_MUL)); | ||||
|         lcd_put_u8str_P(PSTR("  " LCD_STR_FILAM_MUL)); | ||||
|         lcd_put_wchar(':'); | ||||
|         lcd_put_u8str(mstring); | ||||
|         lcd_put_wchar('%'); | ||||
|   | ||||
| @@ -615,36 +615,71 @@ void ST7920_Lite_Status_Screen::draw_feedrate_percentage(const uint8_t percentag | ||||
| void ST7920_Lite_Status_Screen::draw_status_message(const char *str) { | ||||
|   set_ddram_address(DDRAM_LINE_4); | ||||
|   begin_data(); | ||||
|   const uint8_t lcd_len = 16; | ||||
|   #if ENABLED(STATUS_MESSAGE_SCROLLING) | ||||
|     const uint8_t lcd_len = 16; | ||||
|     const uint8_t padding = 2; | ||||
|     uint8_t str_len = strlen(str); | ||||
|  | ||||
|     // Trim whitespace at the end of the str, as for some reason | ||||
|     // messages like "Card Inserted" are padded with many spaces | ||||
|     while (str_len && str[str_len - 1] == ' ') str_len--; | ||||
|     uint8_t slen = utf8_strlen(str); | ||||
|  | ||||
|     if (str_len <= lcd_len) { | ||||
|       // It all fits on the LCD without scrolling | ||||
|     // If the string fits into the LCD, just print it and do not scroll it | ||||
|     if (slen <= lcd_len) { | ||||
|  | ||||
|       // The string isn't scrolling and may not fill the screen | ||||
|       write_str(str); | ||||
|  | ||||
|       // Fill the rest with spaces | ||||
|       while (slen < lcd_len) { | ||||
|         write_byte(' '); | ||||
|         ++slen; | ||||
|       } | ||||
|     } | ||||
|     else { | ||||
|       // Print the message repeatedly until covering the LCD | ||||
|       uint8_t c = status_scroll_pos; | ||||
|       for (uint8_t n = 0; n < lcd_len; n++) { | ||||
|         write_byte(c < str_len ? str[c] : ' '); | ||||
|         c++; | ||||
|         c %= str_len + padding; // Wrap around | ||||
|       // String is larger than the available space in screen. | ||||
|  | ||||
|       // Get a pointer to the next valid UTF8 character | ||||
|       const char *stat = str + status_scroll_offset; | ||||
|  | ||||
|       // Get the string remaining length | ||||
|       const uint8_t rlen = utf8_strlen(stat); | ||||
|  | ||||
|       // If we have enough characters to display | ||||
|       if (rlen >= lcd_len) { | ||||
|         // The remaining string fills the screen - Print it | ||||
|         write_str(stat, lcd_len); | ||||
|       } | ||||
|       else { | ||||
|         // The remaining string does not completely fill the screen | ||||
|         write_str(stat);                        // The string leaves space | ||||
|         uint8_t chars = lcd_len - rlen;         // Amount of space left in characters | ||||
|  | ||||
|         write_byte('.');                        // Always at 1+ spaces left, draw a dot | ||||
|         if (--chars) {                          // Draw a second dot if there's space | ||||
|           write_byte('.'); | ||||
|           if (--chars) | ||||
|             write_str(str, chars);              // Print a second copy of the message | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       // Scroll the message | ||||
|       if (status_scroll_pos == str_len + padding) | ||||
|         status_scroll_pos = 0; | ||||
|       // Adjust by complete UTF8 characters | ||||
|       if (status_scroll_offset < slen) { | ||||
|         status_scroll_offset++; | ||||
|         while (!START_OF_UTF8_CHAR(str[status_scroll_offset])) | ||||
|           status_scroll_offset++; | ||||
|       } | ||||
|       else | ||||
|         status_scroll_pos++; | ||||
|         status_scroll_offset = 0; | ||||
|     } | ||||
|   #else | ||||
|     write_str(str, 16); | ||||
|     // Get the UTF8 character count of the string | ||||
|     uint8_t slen = utf8_strlen(str); | ||||
|  | ||||
|     // Just print the string to the LCD | ||||
|     write_str(str, lcd_len); | ||||
|  | ||||
|     // Fill the rest with spaces if there are missing spaces | ||||
|     while (slen < lcd_len) { | ||||
|       write_byte(' '); | ||||
|       ++slen; | ||||
|     } | ||||
|   #endif | ||||
| } | ||||
|  | ||||
| @@ -792,7 +827,7 @@ void ST7920_Lite_Status_Screen::update_status_or_position(bool forceUpdate) { | ||||
|    */ | ||||
|   if (forceUpdate || status_changed()) { | ||||
|     #if ENABLED(STATUS_MESSAGE_SCROLLING) | ||||
|       status_scroll_pos = 0; | ||||
|       status_scroll_offset = 0; | ||||
|     #endif | ||||
|     #if STATUS_EXPIRE_SECONDS | ||||
|       countdown = lcd_status_message[0] ? STATUS_EXPIRE_SECONDS : 0; | ||||
|   | ||||
| @@ -16,56 +16,14 @@ | ||||
|  | ||||
| #include "fontutils.h" | ||||
|  | ||||
| uint8_t read_byte_ram(uint8_t * str) { return *str; } | ||||
| uint8_t read_byte_rom(uint8_t * str) { return pgm_read_byte(str); } | ||||
| uint8_t read_byte_ram(uint8_t * str) { | ||||
|   return *str; | ||||
| } | ||||
|  | ||||
| #if DEBUG | ||||
|   #ifdef ARDUINO | ||||
|     #include <Arduino.h> | ||||
|     #include <stdarg.h> | ||||
| uint8_t read_byte_rom(uint8_t * str) { | ||||
|   return pgm_read_byte(str); | ||||
| } | ||||
|  | ||||
|     void serial_printf_P(const char *format, ...) { | ||||
|       static char buff[128]; | ||||
|       va_list args; | ||||
|       va_start(args,format); | ||||
|       vsnprintf_P(buff,sizeof(buff),format,args); | ||||
|       va_end(args); | ||||
|       buff[sizeof(buff)/sizeof(buff[0])-1]='\0'; | ||||
|  | ||||
|       //Serial.print(buff); | ||||
|       SERIAL_ECHO(buff); SERIAL_EOL; | ||||
|     } | ||||
|   #endif | ||||
| #endif | ||||
|  | ||||
|  | ||||
| #ifdef __WIN32__                // or whatever | ||||
|   #define PRIiSZ "ld" | ||||
|   #define PRIuSZ "Iu" | ||||
| #else | ||||
|   #define PRIiSZ "zd" | ||||
|   #define PRIuSZ "zu" | ||||
| #endif | ||||
| #define PRIiOFF "lld" | ||||
| #define PRIuOFF "llu" | ||||
|  | ||||
|  | ||||
| #define DBGMSG(a,b, ...) TRACE( #__VA_ARGS__ ) | ||||
|  | ||||
| //typedef int (* pf_bsearch_cb_comp_t)(void *userdata, size_t idx, void * data_pin); /*"data_list[idx] - *data_pin"*/ | ||||
| /** | ||||
|  * @brief 折半方式查找记录 | ||||
|  * | ||||
|  * @param userdata : 用户数据指针 | ||||
|  * @param num_data : 数据个数 | ||||
|  * @param cb_comp : 比较两个数据的回调函数 | ||||
|  * @param data_pinpoint : 所要查找的 匹配数据指针 | ||||
|  * @param ret_idx : 查找到的位置;如果没有找到,则返回如添加该记录时其所在的位置。 | ||||
|  * | ||||
|  * @return 找到则返回0,否则返回<0 | ||||
|  * | ||||
|  * 折半方式查找记录, psl->marr 中指向的数据已经以先小后大方式排好序 | ||||
|  */ | ||||
| /** | ||||
|  * @brief Using binary search to find the position by data_pin | ||||
|  * | ||||
| @@ -82,21 +40,11 @@ uint8_t read_byte_rom(uint8_t * str) { return pgm_read_byte(str); } | ||||
| int pf_bsearch_r(void *userdata, size_t num_data, pf_bsearch_cb_comp_t cb_comp, void *data_pinpoint, size_t *ret_idx) { | ||||
|   int retcomp; | ||||
|  | ||||
|   FU_ASSERT(NULL != ret_idx); | ||||
|   /* 查找合适的位置 */ | ||||
|   if (num_data < 1) { | ||||
|     *ret_idx = 0; | ||||
|     DBGMSG (PFDBG_CATLOG_PF, PFDBG_LEVEL_ERROR, "num_data(%" PRIuSZ ") < 1", num_data); | ||||
|     return -1; | ||||
|   } | ||||
|  | ||||
|   /* 折半查找 */ | ||||
|   /* 为了不出现负数,以免缩小索引的所表示的数据范围 | ||||
|    * (负数表明减少一位二进制位的使用), | ||||
|    * 内部 ileft 和 iright使用从1开始的下标, | ||||
|    *   即1表示C语言中的0, 2表示语言中的1,以此类推。 | ||||
|    * 对外还是使用以 0 为开始的下标 | ||||
|    */ | ||||
|   size_t i = 0, ileft = 1, iright = num_data; | ||||
|   bool flg_found = false; | ||||
|   for (; ileft <= iright;) { | ||||
| @@ -122,28 +70,15 @@ int pf_bsearch_r(void *userdata, size_t num_data, pf_bsearch_cb_comp_t cb_comp, | ||||
|     *ret_idx = i; | ||||
|   else if (ileft >= i + 2) | ||||
|     *ret_idx = i + 1; | ||||
|   //DBGMSG (PFDBG_CATLOG_PF, PFDBG_LEVEL_DEBUG, "not found! num_data=%" PRIuSZ "; ileft=%" PRIuSZ ", iright=%" PRIuSZ ", i=%" PRIuSZ "", num_data, ileft, iright, i); | ||||
|   return -1; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief 转换 UTF-8 编码的一个字符为本地的 Unicode 字符(wchar_t) | ||||
|  * | ||||
|  * @param pstart : 存储 UTF-8 字符的指针 | ||||
|  * @param cb_read_byte : 读取字符的函数;用于8位MCU ROM | ||||
|  * @param pval : 需要返回的 Unicode 字符存放地址指针 | ||||
|  * | ||||
|  * @return 成功返回下个 UTF-8 字符的位置 | ||||
|  * | ||||
|  * 转换 UTF-8 编码的一个字符为本地的 Unicode 字符(wchar_t) | ||||
|  */ | ||||
| /* This function gets the character at the pstart position, interpreting UTF8 multybyte sequences | ||||
|    and returns the pointer to the next character */ | ||||
| uint8_t* get_utf8_value_cb(uint8_t *pstart, read_byte_cb_t cb_read_byte, wchar_t *pval) { | ||||
|   uint32_t val = 0; | ||||
|   uint8_t *p = pstart; | ||||
|  | ||||
|   FU_ASSERT(NULL != pstart); | ||||
|   FU_ASSERT(NULL != cb_read_byte); | ||||
|  | ||||
|   uint8_t valcur = cb_read_byte(p); | ||||
|   if (0 == (0x80 & valcur)) { | ||||
|     val = valcur; | ||||
| @@ -215,113 +150,34 @@ uint8_t* get_utf8_value_cb(uint8_t *pstart, read_byte_cb_t cb_read_byte, wchar_t | ||||
|     val |= (valcur & 0x3F); | ||||
|     p++; | ||||
|   } | ||||
|   else if (0x80 == (0xC0 & valcur)) { | ||||
|     /* error? */ | ||||
|     TRACE("ERR 1"); | ||||
|   else if (0x80 == (0xC0 & valcur)) | ||||
|     for (; 0x80 == (0xC0 & valcur); ) { p++; valcur = cb_read_byte(p); } | ||||
|   } | ||||
|   else { | ||||
|     /* error */ | ||||
|     TRACE("ERR 2"); | ||||
|   else | ||||
|     for (; ((0xFE & valcur) > 0xFC); ) { p++; valcur = cb_read_byte(p); } | ||||
|   } | ||||
|   /* | ||||
|     if (val == 0) { | ||||
|       p = NULL; | ||||
|   */ | ||||
|   /* | ||||
|     } | ||||
|     else if (pstart + maxlen < p) { | ||||
|       p = pstart; | ||||
|       if (pval) *pval = 0; | ||||
|     } | ||||
|   */ | ||||
|  | ||||
|   if (pval) *pval = val; | ||||
|  | ||||
|   return p; | ||||
| } | ||||
|  | ||||
| // uint8_t * get_utf8_value_cb (uint8_t *pstart, read_byte_cb_t cb_read_byte, wchar_t *pval); | ||||
| int utf8_strlen_cb(const char *pstart, read_byte_cb_t cb_read_byte) { | ||||
|   wchar_t ch; | ||||
|   uint8_t *pnext; | ||||
|   int cnt = 0; | ||||
| static inline uint8_t utf8_strlen_cb(const char *pstart, read_byte_cb_t cb_read_byte) { | ||||
|  | ||||
|   for (pnext = (uint8_t *)pstart; ; ) { | ||||
|   uint8_t cnt = 0; | ||||
|   uint8_t *pnext = (uint8_t *)pstart; | ||||
|   for (;;) { | ||||
|     wchar_t ch; | ||||
|     pnext = get_utf8_value_cb(pnext, cb_read_byte, &ch); | ||||
|     if (pnext == NULL || ch == 0) break; | ||||
|     if (!ch) break; | ||||
|     cnt++; | ||||
|     TRACE("cnt=%d, ch=0x%X", cnt, (int)ch); | ||||
|   } | ||||
|   return cnt; | ||||
| } | ||||
|  | ||||
| int | ||||
| my_strlen_P(const char *pstart) | ||||
| { | ||||
|   const char *p; | ||||
|   FU_ASSERT(NULL != pstart); | ||||
|   p = pstart; | ||||
|   while (p && pgm_read_byte(p) != '\0') p ++; | ||||
|   return (p - pstart); | ||||
| uint8_t utf8_strlen(const char *pstart) { | ||||
|   return utf8_strlen_cb(pstart, read_byte_ram); | ||||
| } | ||||
|  | ||||
| uint8_t utf8_strlen(const char *pstart)   { return utf8_strlen_cb(pstart, read_byte_ram); } | ||||
| uint8_t utf8_strlen_P(const char *pstart) { return utf8_strlen_cb(pstart, read_byte_rom); } | ||||
|  | ||||
| char* utf8_strncpy_cb( char * destination, const char *source, size_t num, int len_src, read_byte_cb_t cb_read_byte) { | ||||
|   uint8_t *p = (uint8_t *)source; | ||||
|   uint8_t *d = (uint8_t *)destination; | ||||
|  | ||||
|   FU_ASSERT(NULL != destination); | ||||
|   FU_ASSERT(NULL != source); | ||||
|   FU_ASSERT(NULL != cb_read_byte); | ||||
|  | ||||
|   uint8_t *pend = p + len_src; | ||||
|  | ||||
|   while (p < pend) { | ||||
|     uint8_t valcur = cb_read_byte(p); | ||||
|     size_t len = 0; | ||||
|     if (0 == (0x80 & valcur)) | ||||
|       len = 1; | ||||
|     else if (0xC0 == (0xE0 & valcur)) | ||||
|       len = 2; | ||||
|     else if (0xE0 == (0xF0 & valcur)) | ||||
|       len = 3; | ||||
|     else if (0xF0 == (0xF8 & valcur)) | ||||
|       len = 4; | ||||
|     else if (0xF8 == (0xFC & valcur)) | ||||
|       len = 5; | ||||
|     else if (0xFC == (0xFE & valcur)) | ||||
|       len = 6; | ||||
|     else if (0x80 == (0xC0 & valcur)) { | ||||
|       /* error? */ | ||||
|       for (; 0x80 == (0xC0 & valcur) && (p < pend); ) { p++; valcur = cb_read_byte(p); } | ||||
|     } | ||||
|     else { | ||||
|       /* error */ | ||||
|       for (; ((0xFE & valcur) > 0xFC) && (p < pend); ) { p++; valcur = cb_read_byte(p); } | ||||
|     } | ||||
|     if (len < num) { | ||||
|       for (size_t i = 0; i < len; i++) { | ||||
|         valcur = cb_read_byte(p); | ||||
|         *d = valcur; | ||||
|         d++; | ||||
|         p++; | ||||
|       } | ||||
|     } | ||||
|     else | ||||
|       break; | ||||
|   } | ||||
|   *d = 0; | ||||
|   return destination; | ||||
| uint8_t utf8_strlen_P(const char *pstart) { | ||||
|   return utf8_strlen_cb(pstart, read_byte_rom); | ||||
| } | ||||
|  | ||||
| char* utf8_strncpy(char * destination, const char * source, size_t num) { | ||||
|   return utf8_strncpy_cb(destination, source, num, strlen(source), read_byte_ram); | ||||
| } | ||||
|  | ||||
| char* utf8_strncpy_P(char * destination, const char * source, size_t num) { | ||||
|   return utf8_strncpy_cb(destination, source, num, my_strlen_P(source), read_byte_rom); | ||||
| } | ||||
|   | ||||
| @@ -9,151 +9,40 @@ | ||||
| #ifndef _FONT_UTILS_H | ||||
| #define _FONT_UTILS_H | ||||
|  | ||||
| #define DEBUG 0 | ||||
|  | ||||
| #ifdef ARDUINO | ||||
|   #include <Arduino.h> | ||||
| #else // ARDUINO | ||||
|   #include <stdint.h> | ||||
|   #include <stdio.h> | ||||
|   #include <stdlib.h> | ||||
| #endif // ARDUINO | ||||
|  | ||||
| #ifndef pgm_read_word_near // __AVR__ | ||||
|   #include <stdint.h> | ||||
|   #include <string.h> | ||||
|   #include <assert.h> | ||||
|   //#define pgm_read_word_near(a) *((uint16_t *)(a)) | ||||
|   #define pgm_read_word_near(a) (*(a)) | ||||
|   #define pgm_read_byte_near(a) *((uint8_t *)(a)) | ||||
|   #define pgm_read_byte pgm_read_byte_near | ||||
| #elif defined(__AVR__) | ||||
|   #include <avr/pgmspace.h> | ||||
| #endif | ||||
|  | ||||
| #ifndef PROGMEM | ||||
|   #define PROGMEM | ||||
|   #define strlen_P strlen | ||||
|   #define memcpy_P memcpy | ||||
|   #define vsnprintf_P vsnprintf | ||||
| #endif // PROGMEM | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
| // read a byte from ROM or RAM | ||||
| typedef uint8_t (* read_byte_cb_t)(uint8_t * str); | ||||
|  | ||||
| //inline uint8_t read_byte_ram(uint8_t * str) { return *str; } | ||||
| //inline uint8_t read_byte_rom(uint8_t * str) { return pgm_read_byte(str); } | ||||
| uint8_t read_byte_ram(uint8_t * str); | ||||
| uint8_t read_byte_rom(uint8_t * str); | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #include "../core/macros.h" | ||||
| #include <stddef.h> // wchar_t | ||||
| #include <stdint.h> // uint32_t | ||||
|  | ||||
| #ifdef ARDUINO | ||||
| // read a byte from ROM or RAM | ||||
| typedef uint8_t (*read_byte_cb_t)(uint8_t * str); | ||||
|  | ||||
|   // there's overflow of the wchar_t due to the 2-byte size in Arduino | ||||
|   // sizeof(wchar_t)=2; sizeof(size_t)=2; sizeof(uint32_t)=4; | ||||
|   // sizeof(int)=2; sizeof(long)=4; sizeof(unsigned)=2; | ||||
|   //#undef wchar_t | ||||
|   #define wchar_t uint32_t | ||||
|   //typedef uint32_t wchar_t; | ||||
| uint8_t read_byte_ram(uint8_t * str); | ||||
| uint8_t read_byte_rom(uint8_t * str); | ||||
|  | ||||
| #else | ||||
|  | ||||
|   #include <sys/types.h> // ssize_t | ||||
|   #include <assert.h> | ||||
|   // x86_64 | ||||
|   // sizeof(wchar_t)=4; sizeof(size_t)=8; sizeof(uint32_t)=4; | ||||
|   // sizeof(int)=4; sizeof(long)=8; sizeof(unsigned)=4; | ||||
|   //#define wchar_t uint32_t | ||||
|   #define wchar_t size_t | ||||
|  | ||||
|   #ifndef PRIu32 | ||||
|     #define PRIu32 "lu" | ||||
|   #endif | ||||
|   #ifndef PRIX32 | ||||
|     #define PRIX32 "lX" | ||||
|   #endif | ||||
|  | ||||
| #endif | ||||
|  | ||||
| #define UNUSED_VARIABLE(a) ((void)(a)) | ||||
|  | ||||
| #ifndef MIN | ||||
|   #define MIN(a,b) (((a)>(b))?(b):(a)) | ||||
| #endif | ||||
| // there's overflow of the wchar_t due to the 2-byte size in Arduino | ||||
| // sizeof(wchar_t)=2; sizeof(size_t)=2; sizeof(uint32_t)=4; | ||||
| // sizeof(int)=2; sizeof(long)=4; sizeof(unsigned)=2; | ||||
| //#undef wchar_t | ||||
| #define wchar_t uint32_t | ||||
| //typedef uint32_t wchar_t; | ||||
|  | ||||
| #ifndef NUM_ARRAY | ||||
|   #define NUM_ARRAY(a) (sizeof(a)/sizeof((a)[0])) | ||||
| #endif // NUM_ARRAY | ||||
|  | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
| //#define pixel_len_t u8g_uint_t | ||||
| #define pixel_len_t uint16_t | ||||
| //#define pixel_len_t uint8_t | ||||
| //typedef uint16_t pixel_len_t; | ||||
| typedef uint16_t pixel_len_t; | ||||
| #define PIXEL_LEN_NOLIMIT ((pixel_len_t)(-1)) | ||||
|  | ||||
| /* Perform binary search */ | ||||
| typedef int (* pf_bsearch_cb_comp_t)(void *userdata, size_t idx, void * data_pin); /*"data_list[idx] - *data_pin"*/ | ||||
| int pf_bsearch_r(void *userdata, size_t num_data, pf_bsearch_cb_comp_t cb_comp, void *data_pinpoint, size_t *ret_idx); | ||||
|  | ||||
| //wchar_t get_val_utf82uni(uint8_t *pstart); | ||||
| //uint8_t * get_utf8_value(uint8_t *pstart, wchar_t *pval); | ||||
| uint8_t * get_utf8_value_cb(uint8_t *pstart, read_byte_cb_t cb_read_byte, wchar_t *pval); | ||||
| /* Get the character, decoding multibyte UTF8 characters and returning a pointer to the start of the next UTF8 character */ | ||||
| uint8_t* get_utf8_value_cb(uint8_t *pstart, read_byte_cb_t cb_read_byte, wchar_t *pval); | ||||
|  | ||||
| /* Returns lenght of string in CHARACTERS, NOT BYTES */ | ||||
| uint8_t utf8_strlen(const char *pstart); | ||||
| uint8_t utf8_strlen_P(const char *pstart); | ||||
|  | ||||
| char * utf8_strncpy(char * destination, const char * source, size_t num); | ||||
| char * utf8_strncpy_P(char * destination, const char * source, size_t num); | ||||
| int my_strlen_P(const char *pstart); | ||||
|  | ||||
| #if 0 // DEBUG | ||||
| #if 0 //defined(ARDUINO) | ||||
| #if defined(__AVR__) | ||||
| #define TRACE(fmt, ...) {static const PROGMEM char CONSTSTR[] = "%d %d " fmt " {ln:%d;}\n"; serial_printf_P(CONSTSTR, millis(), ##__VA_ARGS__, __LINE__);  } | ||||
| #else | ||||
| #define TRACE(fmt, ...) {static const PROGMEM char CONSTSTR[] = "%d " fmt " {ln:%d, fn:" __FILE__ "}\n"; serial_printf_P(CONSTSTR, millis(), ##__VA_ARGS__, __LINE__);  } | ||||
| #endif | ||||
| #define FU_ASSERT(a) if (!(a)) {TRACE("Assert: " # a ); } | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| void serial_printf_P(const char *format, ...); | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #else // ARDUINO | ||||
| #include <stdio.h> | ||||
| #define FU_ASSERT(a) if (!(a)) {printf("Assert: " # a); exit(1);} | ||||
| #define TRACE(fmt, ...) fprintf(stdout, "[%s()] " fmt " {ln:%d, fn:" __FILE__ "}\n", __func__, ##__VA_ARGS__, __LINE__) | ||||
| //#else | ||||
| //#define FU_ASSERT(a) | ||||
| //#define TRACE(...) | ||||
| #endif // ARDUINO | ||||
|  | ||||
| #else // DEBUG | ||||
|   #define TRACE(fmt, ...) | ||||
|   #define FU_ASSERT(a) | ||||
| #endif // DEBUG | ||||
|  | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #endif // _FONT_UTILS_H | ||||
|   | ||||
| @@ -17,11 +17,7 @@ | ||||
|   #include "u8g_fontutf8.h" | ||||
| #endif | ||||
|  | ||||
| #define PRINTABLE(C) (((C) & 0xC0u) != 0x80u) | ||||
|  | ||||
| #ifdef __cplusplus | ||||
|   extern "C" { | ||||
| #endif | ||||
| #define START_OF_UTF8_CHAR(C) (((C) & 0xC0u) != 0x80u) | ||||
|  | ||||
| int lcd_glyph_height(void); | ||||
|  | ||||
| @@ -49,15 +45,11 @@ int lcd_put_u8str_max(const char * utf8_str, pixel_len_t max_length); | ||||
|  * | ||||
|  * Draw a ROM UTF-8 string | ||||
|  */ | ||||
| int lcd_put_u8str_max_rom(const char * utf8_str_P, pixel_len_t max_length); | ||||
| int lcd_put_u8str_max_P(const char * utf8_str_P, pixel_len_t max_length); | ||||
|  | ||||
| void lcd_moveto(int col, int row); | ||||
|  | ||||
| #ifdef __cplusplus | ||||
|   } | ||||
| #endif | ||||
|  | ||||
| #define lcd_put_u8str_rom(str) lcd_put_u8str_max_rom(str, PIXEL_LEN_NOLIMIT) | ||||
| inline int lcd_put_u8str_P(const char *str) { return lcd_put_u8str_max_P(str, PIXEL_LEN_NOLIMIT); } | ||||
|  | ||||
| inline int lcd_put_u8str(const char* str) { return lcd_put_u8str_max(str, PIXEL_LEN_NOLIMIT); } | ||||
|  | ||||
|   | ||||
| @@ -24,20 +24,13 @@ | ||||
| #include "fontutils.h" | ||||
| #include "lcdprint.h" | ||||
|  | ||||
| #if defined(ARDUINO) | ||||
|   #include "ultralcd_common_HD44780.h" | ||||
|   #ifndef LCD_CLASS | ||||
|     #include <LiquidCrystal.h> | ||||
|     #define LCD_CLASS LiquidCrystal | ||||
|   #endif | ||||
|   extern LCD_CLASS lcd; | ||||
|   LCD_CLASS *plcd = &lcd; | ||||
|   #define _lcd_write(a) plcd->write(a) | ||||
|   #define _lcd_setcursor(col, row) plcd->setCursor((col), (row)); | ||||
| #else | ||||
|   #define _lcd_write(a) TRACE("Write LCD: %c (%d)", (a), (int)(a)); | ||||
|   #define _lcd_setcursor(col, row) TRACE("Set cursor LCD: (%d,%d)", (col), (row)); | ||||
| #include "ultralcd_common_HD44780.h" | ||||
| #ifndef LCD_CLASS | ||||
|   #include <LiquidCrystal.h> | ||||
|   #define LCD_CLASS LiquidCrystal | ||||
| #endif | ||||
| extern LCD_CLASS lcd; | ||||
| LCD_CLASS *plcd = &lcd; | ||||
|  | ||||
| int lcd_glyph_height(void) { return 1; } | ||||
|  | ||||
| @@ -878,25 +871,10 @@ static const hd44780_charmap_t g_hd44780_charmap_common[] PROGMEM = { | ||||
|  | ||||
| /* return v1 - v2 */ | ||||
| static int hd44780_charmap_compare(hd44780_charmap_t * v1, hd44780_charmap_t * v2) { | ||||
|   FU_ASSERT(NULL != v1); | ||||
|   FU_ASSERT(NULL != v2); | ||||
|   TRACE("compare char1 %" PRIu32 "(0x%" PRIX32 ")", v1->uchar, v1->uchar); | ||||
|   TRACE("compare char2 %" PRIu32 "(0x%" PRIX32 ")", v2->uchar, v2->uchar); | ||||
|   if (v1->uchar < v2->uchar) { | ||||
|     TRACE("compare return -1"); | ||||
|   if (v1->uchar < v2->uchar) | ||||
|     return -1; | ||||
|   } else if (v1->uchar > v2->uchar) { | ||||
|     TRACE("compare return 1"); | ||||
|   else if (v1->uchar > v2->uchar) | ||||
|     return 1; | ||||
|   } | ||||
|   #if 0 | ||||
|     if (v1->idx < v2->idx) { | ||||
|       return -1; | ||||
|     } else if (v1->idx > v2->idx) { | ||||
|       return 1; | ||||
|     } | ||||
|   #endif | ||||
|   TRACE("compare return 0"); | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| @@ -909,9 +887,7 @@ static int pf_bsearch_cb_comp_hd4map_pgm(void *userdata, size_t idx, void * data | ||||
|  | ||||
| #if DEBUG | ||||
|  | ||||
| int | ||||
| test_hd44780_charmap(hd44780_charmap_t *data, size_t size, char *name, char flg_show_contents) | ||||
| { | ||||
| int test_hd44780_charmap(hd44780_charmap_t *data, size_t size, char *name, char flg_show_contents) { | ||||
|   int ret; | ||||
|   size_t idx = 0; | ||||
|   hd44780_charmap_t preval = {0, 0, 0}; | ||||
| @@ -963,9 +939,7 @@ test_hd44780_charmap(hd44780_charmap_t *data, size_t size, char *name, char flg_ | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| int | ||||
| test_hd44780_charmap_all(void) | ||||
| { | ||||
| int test_hd44780_charmap_all(void) { | ||||
|   int flg_error = 0; | ||||
|   if (test_hd44780_charmap(g_hd44780_charmap_device, NUM_ARRAY(g_hd44780_charmap_device), "g_hd44780_charmap_device", 0) < 0) { | ||||
|     flg_error = 1; | ||||
| @@ -986,18 +960,17 @@ test_hd44780_charmap_all(void) | ||||
| #endif // DEBUG | ||||
|  | ||||
| void lcd_moveto(int col, int row) { | ||||
|   TRACE("Move to: (%d,%d)", col, row); | ||||
|   _lcd_setcursor(col, row); | ||||
|   plcd->setCursor(col, row); | ||||
| } | ||||
|  | ||||
| // return < 0 on error | ||||
| // return the advanced cols | ||||
| int lcd_put_wchar_max(wchar_t c, pixel_len_t max_length) { | ||||
|  | ||||
|   // find the HD44780 internal ROM first | ||||
|   int ret; | ||||
|   size_t idx = 0; | ||||
|   hd44780_charmap_t pinval; | ||||
|   hd44780_charmap_t localval; | ||||
|   hd44780_charmap_t *copy_address = NULL; | ||||
|   pinval.uchar = c; | ||||
|   pinval.idx = -1; | ||||
| @@ -1006,37 +979,33 @@ int lcd_put_wchar_max(wchar_t c, pixel_len_t max_length) { | ||||
|  | ||||
|   // TODO: fix the '\\' that doesnt exist in the HD44870 | ||||
|   if (c < 128) { | ||||
|     //TRACE("draw char: regular %d", (int)c); | ||||
|     _lcd_write((uint8_t)c); | ||||
|     plcd->write((uint8_t)c); | ||||
|     return 1; | ||||
|   } | ||||
|   copy_address = NULL; | ||||
|   ret = pf_bsearch_r((void *)g_hd44780_charmap_device, NUM_ARRAY(g_hd44780_charmap_device), pf_bsearch_cb_comp_hd4map_pgm, (void *)&pinval, &idx); | ||||
|   if (ret >= 0) { | ||||
|     copy_address = (hd44780_charmap_t *)(g_hd44780_charmap_device + idx); | ||||
|   } else { | ||||
|   } | ||||
|   else { | ||||
|     ret = pf_bsearch_r((void *)g_hd44780_charmap_common, NUM_ARRAY(g_hd44780_charmap_common), pf_bsearch_cb_comp_hd4map_pgm, (void *)&pinval, &idx); | ||||
|     if (ret >= 0) { | ||||
|       copy_address = (hd44780_charmap_t *)(g_hd44780_charmap_common + idx); | ||||
|     } | ||||
|     if (ret >= 0) copy_address = (hd44780_charmap_t *)(g_hd44780_charmap_common + idx); | ||||
|   } | ||||
|  | ||||
|   if (ret >= 0) { | ||||
|     hd44780_charmap_t localval; | ||||
|     // found | ||||
|     FU_ASSERT(NULL != copy_address); | ||||
|     memcpy_P(&localval, copy_address, sizeof(localval)); | ||||
|     FU_ASSERT((localval.uchar == c) && (localval.uchar == pinval.uchar)); | ||||
|     TRACE("draw char: %" PRIu32 "(0x%" PRIX32 ") at ROM %d(+%d)", c, c, (int)localval.idx, (int)localval.idx2); | ||||
|     _lcd_write(localval.idx); | ||||
|     plcd->write(localval.idx); | ||||
|     if (max_length >= 2 && localval.idx2 > 0) { | ||||
|       _lcd_write(localval.idx2); | ||||
|       plcd->write(localval.idx2); | ||||
|       return 2; | ||||
|     } | ||||
|     return 1; | ||||
|   } | ||||
|   // print '?' instead | ||||
|   TRACE("draw char: Not found " PRIu32 "(0x%" PRIX32 ")", c, c); | ||||
|   _lcd_write((uint8_t)'?'); | ||||
|  | ||||
|   // Not found, print '?' instead | ||||
|   plcd->write((uint8_t)'?'); | ||||
|   return 1; | ||||
| } | ||||
|  | ||||
| @@ -1044,7 +1013,6 @@ int lcd_put_wchar_max(wchar_t c, pixel_len_t max_length) { | ||||
| * @brief Draw a UTF-8 string | ||||
| * | ||||
| * @param utf8_str : the UTF-8 string | ||||
| * @param len : the byte length of the string (returned by strlen(utf8_str) or strlen_P(utf8_str) ) | ||||
| * @param cb_read_byte : the callback function to read one byte from the utf8_str (from RAM or ROM) | ||||
| * @param max_length : the pixel length of the string allowed (or number of slots in HD44780) | ||||
| * | ||||
| @@ -1052,39 +1020,24 @@ int lcd_put_wchar_max(wchar_t c, pixel_len_t max_length) { | ||||
| * | ||||
| * Draw a UTF-8 string | ||||
| */ | ||||
| static int lcd_put_u8str_max_cb(const char * utf8_str, uint16_t len, uint8_t (*cb_read_byte)(uint8_t * str), pixel_len_t max_length) { | ||||
|   wchar_t ch; | ||||
|   uint8_t *p, *pend; | ||||
| static int lcd_put_u8str_max_cb(const char * utf8_str, uint8_t (*cb_read_byte)(uint8_t * str), pixel_len_t max_length) { | ||||
|   pixel_len_t ret = 0; | ||||
|  | ||||
|   TRACE("BEGIN lcd_put_u8str_max_cb(len=%d, maxlen=%d)", len, max_length); | ||||
|   pend = (uint8_t *)utf8_str + len; | ||||
|   for (p = (uint8_t *)utf8_str; (p < pend) && (ret < max_length); ) { | ||||
|     ch = 0; | ||||
|   uint8_t *p = (uint8_t *)utf8_str; | ||||
|   while (ret < max_length) { | ||||
|     wchar_t ch = 0; | ||||
|     p = get_utf8_value_cb(p, cb_read_byte, &ch); | ||||
|     if (NULL == p) { | ||||
|       TRACE("No more char, break ..."); | ||||
|       break; | ||||
|     } | ||||
|     FU_ASSERT(ret < max_length); | ||||
|     if (!p) break; | ||||
|     ret += lcd_put_wchar_max(ch, max_length - ret); | ||||
|   } | ||||
|   return (int)ret; | ||||
| } | ||||
|  | ||||
| int lcd_put_u8str_max(const char * utf8_str, pixel_len_t max_length) { | ||||
|   //TRACE("BEGIN lcd_put_u8str_max(str='%s', len=%d, maxlen=%d)", utf8_str, strlen(utf8_str), max_length); | ||||
|   TRACE("BEGIN lcd_put_u8str_max(str='%s')", utf8_str); | ||||
|   TRACE("BEGIN lcd_put_u8str_max('len=%d)", strlen(utf8_str)); | ||||
|   TRACE("BEGIN lcd_put_u8str_max(maxlen=%d)", max_length); | ||||
|   return lcd_put_u8str_max_cb(utf8_str, strlen(utf8_str), read_byte_ram, max_length); | ||||
|   return lcd_put_u8str_max_cb(utf8_str, read_byte_ram, max_length); | ||||
| } | ||||
|  | ||||
| int lcd_put_u8str_max_rom(const char * utf8_str_P, pixel_len_t max_length) { | ||||
|   //TRACE("BEGIN lcd_put_u8str_max_rom('%s', len=%d, maxlen=%d)", utf8_str_P, strlen_P(utf8_str_P), max_length); | ||||
|   TRACE("BEGIN lcd_put_u8str_max_rom(len=%d)", strlen_P(utf8_str_P)); | ||||
|   TRACE("BEGIN lcd_put_u8str_max_rom(maxlen=%d)", max_length); | ||||
|   return lcd_put_u8str_max_cb(utf8_str_P, strlen_P(utf8_str_P), read_byte_rom, max_length); | ||||
| int lcd_put_u8str_max_P(const char * utf8_str_P, pixel_len_t max_length) { | ||||
|   return lcd_put_u8str_max_cb(utf8_str_P, read_byte_rom, max_length); | ||||
| } | ||||
|  | ||||
| #endif // DOGLCD | ||||
|   | ||||
| @@ -25,25 +25,22 @@ extern U8GLIB *pu8g; | ||||
|  | ||||
| int lcd_glyph_height(void) { | ||||
|   return u8g_GetFontBBXHeight(pu8g->getU8g()); | ||||
|   //return u8g_GetFontBBXOffY(pu8g->getU8g()); | ||||
| } | ||||
|  | ||||
| void lcd_moveto(int col, int row) { | ||||
|   TRACE("Move to: (%d,%d)", col, row); | ||||
|   _lcd_setcursor(col, row); | ||||
| } | ||||
|  | ||||
| // return < 0 on error | ||||
| // return the advanced pixels | ||||
| int lcd_put_wchar_max(wchar_t c, pixel_len_t max_length) { | ||||
|   if (c < 256) { | ||||
|     TRACE("draw char: regular %d", (int)c); | ||||
|     _lcd_write((char)c); | ||||
|     return u8g_GetFontBBXWidth(pu8g->getU8g()); | ||||
|   } | ||||
|   unsigned int x = pu8g->getPrintCol(), | ||||
|                y = pu8g->getPrintRow(), | ||||
|                ret = uxg_DrawWchar(pu8g->getU8g(), x, y, c, max_length); | ||||
|   TRACE("uxg_DrawWchar(x=%d,y=%d,maxlen=%d", x, y, max_length); | ||||
|   TRACE("u8g->setPrintPos(x=%d + ret=%d,y=%d", x, ret, y); | ||||
|   pu8g->setPrintPos(x + ret, y); | ||||
|  | ||||
|   return ret; | ||||
| @@ -53,25 +50,16 @@ int lcd_put_u8str_max(const char * utf8_str, pixel_len_t max_length) { | ||||
|   unsigned int x = pu8g->getPrintCol(), | ||||
|                y = pu8g->getPrintRow(), | ||||
|                ret = uxg_DrawUtf8Str(pu8g->getU8g(), x, y, utf8_str, max_length); | ||||
|   TRACE("uxg_DrawUtf8Str(x=%d,y=%d,maxlen=%d", x, y, max_length); | ||||
|   TRACE("u8g->setPrintPos(x=%d + ret=%d,y=%d", x, ret, y); | ||||
|   pu8g->setPrintPos(x + ret, y); | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| int lcd_put_u8str_max_rom(const char * utf8_str_P, pixel_len_t max_length) { | ||||
| int lcd_put_u8str_max_P(const char * utf8_str_P, pixel_len_t max_length) { | ||||
|   unsigned int x = pu8g->getPrintCol(), | ||||
|                y = pu8g->getPrintRow(), | ||||
|                ret = uxg_DrawUtf8StrP(pu8g->getU8g(), x, y, utf8_str_P, max_length); | ||||
|   TRACE("uxg_DrawUtf8StrP(x=%d,y=%d,maxlen=%d", x, y, max_length); | ||||
|   TRACE("u8g->setPrintPos(x=%d + ret=%d,y=%d", x, ret, y); | ||||
|   pu8g->setPrintPos(x + ret, y); | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| #else // !DOGLCD | ||||
|  | ||||
|   #define _lcd_write(a) TRACE("Write LCD: %c (%d)", (a), (int)(a)); | ||||
|   #define _lcd_setcursor(col, row) TRACE("Set cursor LCD: (%d,%d)", (col), (row)); | ||||
|  | ||||
| #endif // !DOGLCD | ||||
| #endif // DOGLCD | ||||
|   | ||||
| @@ -18,28 +18,6 @@ | ||||
| //////////////////////////////////////////////////////////// | ||||
| typedef void font_t; | ||||
|  | ||||
| #ifndef PSTR | ||||
| #define PSTR(a) a | ||||
|  | ||||
| void* memcpy_from_rom(void *dest, const void * rom_src, size_t sz) { | ||||
|   uint8_t * p; | ||||
|   uint8_t * s; | ||||
|  | ||||
|   FU_ASSERT(NULL != dest); | ||||
|   p = (uint8_t*)dest; | ||||
|   s = (uint8_t*)rom_src; | ||||
|   uint8_t c; | ||||
|   while ((p - (uint8_t *)dest) < sz) { | ||||
|     *p = pgm_read_byte(s); | ||||
|     p ++; | ||||
|     s ++; | ||||
|   } | ||||
|   return p; | ||||
| } | ||||
| #else | ||||
| #define memcpy_from_rom memcpy_P | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|  * @brief the callback function to draw something | ||||
|  * | ||||
| @@ -53,25 +31,14 @@ void* memcpy_from_rom(void *dest, const void * rom_src, size_t sz) { | ||||
|  */ | ||||
| typedef int (* fontgroup_cb_draw_t)(void *userdata, const font_t *fnt_current, const char *msg); | ||||
|  | ||||
| //extern int fontgroup_init(font_group_t * root, const uxg_fontinfo_t * fntinfo, int number); | ||||
| //extern int fontgroup_drawstring(font_group_t *group, const font_t *fnt_default, const char *utf8_msg, void *userdata, fontgroup_cb_draw_t cb_draw); | ||||
| //extern uxg_fontinfo_t* fontgroup_first(font_group_t * root); | ||||
|  | ||||
|  | ||||
| //////////////////////////////////////////////////////////// | ||||
| /* return v1 - v2 */ | ||||
| static int fontinfo_compare(uxg_fontinfo_t * v1, uxg_fontinfo_t * v2) { | ||||
|   FU_ASSERT(NULL != v1); | ||||
|   FU_ASSERT(NULL != v2); | ||||
|   if (v1->page < v2->page) | ||||
|     return -1; | ||||
|   else if (v1->page > v2->page) | ||||
|     return 1; | ||||
|   if (v1->page < v2->page)      return -1; | ||||
|   else if (v1->page > v2->page) return 1; | ||||
|  | ||||
|   if (v1->end < v2->begin) | ||||
|     return -1; | ||||
|   else if (v1->begin > v2->end) | ||||
|     return 1; | ||||
|   if (v1->end < v2->begin)      return -1; | ||||
|   else if (v1->begin > v2->end) return 1; | ||||
|  | ||||
|   return 0; | ||||
| } | ||||
| @@ -80,7 +47,7 @@ static int fontinfo_compare(uxg_fontinfo_t * v1, uxg_fontinfo_t * v2) { | ||||
| static int pf_bsearch_cb_comp_fntifo_pgm (void *userdata, size_t idx, void *data_pin) { | ||||
|   uxg_fontinfo_t *fntinfo = (uxg_fontinfo_t*)userdata; | ||||
|   uxg_fontinfo_t localval; | ||||
|   memcpy_from_rom(&localval, fntinfo + idx, sizeof(localval)); | ||||
|   memcpy_P(&localval, fntinfo + idx, sizeof(localval)); | ||||
|   return fontinfo_compare(&localval, (uxg_fontinfo_t*)data_pin); | ||||
| } | ||||
|  | ||||
| @@ -92,7 +59,6 @@ typedef struct _font_group_t { | ||||
| static int fontgroup_init(font_group_t * root, const uxg_fontinfo_t * fntinfo, int number) { | ||||
|   root->m_fntifo = fntinfo; | ||||
|   root->m_fntinfo_num = number; | ||||
|  | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| @@ -105,26 +71,23 @@ static const font_t* fontgroup_find(font_group_t * root, wchar_t val) { | ||||
|   if (pf_bsearch_r((void*)root->m_fntifo, root->m_fntinfo_num, pf_bsearch_cb_comp_fntifo_pgm, (void*)&vcmp, &idx) < 0) | ||||
|     return NULL; | ||||
|  | ||||
|   memcpy_from_rom(&vcmp, root->m_fntifo + idx, sizeof(vcmp)); | ||||
|   memcpy_P(&vcmp, root->m_fntifo + idx, sizeof(vcmp)); | ||||
|   return vcmp.fntdata; | ||||
| } | ||||
|  | ||||
| static void fontgroup_drawwchar(font_group_t *group, const font_t *fnt_default, wchar_t val, void * userdata, fontgroup_cb_draw_t cb_draw_ram) { | ||||
|   uint8_t buf[2] = {0, 0}; | ||||
|   const font_t * fntpqm = NULL; | ||||
|  | ||||
|   TRACE("fontgroup_drawwchar char=%d(0x%X)", (int)val, (int)val); | ||||
|   fntpqm = (font_t*)fontgroup_find(group, val); | ||||
|   if (NULL == fntpqm) { | ||||
|   const font_t * fntpqm = (font_t*)fontgroup_find(group, val); | ||||
|   if (!fntpqm) { | ||||
|     // Unknown char, use default font | ||||
|     buf[0] = (uint8_t)(val & 0xFF); | ||||
|     fntpqm = fnt_default; | ||||
|     TRACE("Unknown char %d(0x%X), use default font", (int)val, (int)val); | ||||
|   } | ||||
|   if (fnt_default != fntpqm) { | ||||
|     buf[0] = (uint8_t)(val & 0x7F); | ||||
|     buf[0] |= 0x80; // use upper page to avoid 0x00 error in C. you may want to generate the font data | ||||
|   } | ||||
|   //TRACE("set font: %p; (default=%p)", fntpqm, UXG_DEFAULT_FONT); | ||||
|  | ||||
|   cb_draw_ram (userdata, fntpqm, (char*) buf); | ||||
| } | ||||
|  | ||||
| @@ -142,31 +105,27 @@ static void fontgroup_drawwchar(font_group_t *group, const font_t *fnt_default, | ||||
|  * | ||||
|  * Get the screen pixel width of a ROM UTF-8 string | ||||
|  */ | ||||
| static void fontgroup_drawstring(font_group_t *group, const font_t *fnt_default, const char *utf8_msg, int len_msg, read_byte_cb_t cb_read_byte, void * userdata, fontgroup_cb_draw_t cb_draw_ram) { | ||||
|   uint8_t *pend = (uint8_t*)utf8_msg + len_msg; | ||||
|   for (uint8_t *p = (uint8_t*)utf8_msg; p < pend; ) { | ||||
| static void fontgroup_drawstring(font_group_t *group, const font_t *fnt_default, const char *utf8_msg, read_byte_cb_t cb_read_byte, void * userdata, fontgroup_cb_draw_t cb_draw_ram) { | ||||
|   uint8_t *p = (uint8_t*)utf8_msg; | ||||
|   for (;;) { | ||||
|     wchar_t val = 0; | ||||
|     p = get_utf8_value_cb(p, cb_read_byte, &val); | ||||
|     if (NULL == p) { | ||||
|       TRACE("No more char, break ..."); | ||||
|       break; | ||||
|     } | ||||
|     if (!val) break; | ||||
|     fontgroup_drawwchar(group, fnt_default, val, userdata, cb_draw_ram); | ||||
|   } | ||||
| } | ||||
|  | ||||
| //////////////////////////////////////////////////////////// | ||||
| static char flag_fontgroup_inited1 = 0; | ||||
| #define flag_fontgroup_inited flag_fontgroup_inited1 | ||||
| static bool flag_fontgroup_was_inited = false; | ||||
| static font_group_t g_fontgroup_root = {NULL, 0}; | ||||
|  | ||||
| /** | ||||
|  * @brief check if font is loaded | ||||
|  */ | ||||
| char uxg_Utf8FontIsInited(void) { return flag_fontgroup_inited; } | ||||
| static inline bool uxg_Utf8FontIsInited(void) { return flag_fontgroup_was_inited; } | ||||
|  | ||||
| int uxg_SetUtf8Fonts (const uxg_fontinfo_t * fntinfo, int number) { | ||||
|   flag_fontgroup_inited = 1; | ||||
|   flag_fontgroup_was_inited = 1; | ||||
|   return fontgroup_init(&g_fontgroup_root, fntinfo, number); | ||||
| } | ||||
|  | ||||
| @@ -179,22 +138,17 @@ struct _uxg_drawu8_data_t { | ||||
|   const void * fnt_prev; | ||||
| }; | ||||
|  | ||||
| static int fontgroup_cb_draw_u8g (void *userdata, const font_t *fnt_current, const char *msg) { | ||||
| static int fontgroup_cb_draw_u8g(void *userdata, const font_t *fnt_current, const char *msg) { | ||||
|   struct _uxg_drawu8_data_t * pdata = (_uxg_drawu8_data_t*)userdata; | ||||
|  | ||||
|   FU_ASSERT(NULL != userdata); | ||||
|   if (pdata->fnt_prev != fnt_current) { | ||||
|     u8g_SetFont(pdata->pu8g, (const u8g_fntpgm_uint8_t*)fnt_current); | ||||
|     //u8g_SetFontPosBottom(pdata->pu8g); | ||||
|     pdata->fnt_prev = fnt_current; | ||||
|   } | ||||
|   if ((pdata->max_width != PIXEL_LEN_NOLIMIT) && (pdata->adv + u8g_GetStrPixelWidth(pdata->pu8g, (char*)msg) > pdata->max_width)) { | ||||
|     TRACE("return end, adv=%d, width=%d, maxlen=%d", pdata->adv, u8g_GetStrPixelWidth(pdata->pu8g, (char*)msg), pdata->max_width); | ||||
|   if ((pdata->max_width != PIXEL_LEN_NOLIMIT) && (pdata->adv + u8g_GetStrPixelWidth(pdata->pu8g, (char*)msg) > pdata->max_width)) | ||||
|     return 1; | ||||
|   } | ||||
|   TRACE("Draw string 0x%X", (int)msg[0]); | ||||
|   pdata->adv += u8g_DrawStr(pdata->pu8g, pdata->x + pdata->adv, pdata->y, (char*) msg); | ||||
|   //TRACE("adv pos= %d", pdata->adv); | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| @@ -260,7 +214,7 @@ unsigned int uxg_DrawUtf8Str(u8g_t *pu8g, unsigned int x, unsigned int y, const | ||||
|   data.adv = 0; | ||||
|   data.max_width = max_width; | ||||
|   data.fnt_prev = NULL; | ||||
|   fontgroup_drawstring(group, fnt_default, utf8_msg, strlen(utf8_msg), read_byte_ram, (void*)&data, fontgroup_cb_draw_u8g); | ||||
|   fontgroup_drawstring(group, fnt_default, utf8_msg, read_byte_ram, (void*)&data, fontgroup_cb_draw_u8g); | ||||
|   u8g_SetFont(pu8g, (const u8g_fntpgm_uint8_t*)fnt_default); | ||||
|  | ||||
|   return data.adv; | ||||
| @@ -285,7 +239,6 @@ unsigned int uxg_DrawUtf8StrP(u8g_t *pu8g, unsigned int x, unsigned int y, const | ||||
|   const font_t *fnt_default = uxg_GetFont(pu8g); | ||||
|  | ||||
|   if (!uxg_Utf8FontIsInited()) { | ||||
|     TRACE("Error, utf8string not inited!"); | ||||
|     u8g_DrawStrP(pu8g, x, y, (const u8g_pgm_uint8_t *)PSTR("Err: utf8 font not initialized.")); | ||||
|     return 0; | ||||
|   } | ||||
| @@ -295,19 +248,15 @@ unsigned int uxg_DrawUtf8StrP(u8g_t *pu8g, unsigned int x, unsigned int y, const | ||||
|   data.adv = 0; | ||||
|   data.max_width = max_width; | ||||
|   data.fnt_prev = NULL; | ||||
|   TRACE("call fontgroup_drawstring"); | ||||
|   fontgroup_drawstring(group, fnt_default, utf8_msg, my_strlen_P(utf8_msg), read_byte_rom, (void*)&data, fontgroup_cb_draw_u8g); | ||||
|   TRACE("restore font"); | ||||
|   fontgroup_drawstring(group, fnt_default, utf8_msg, read_byte_rom, (void*)&data, fontgroup_cb_draw_u8g); | ||||
|   u8g_SetFont(pu8g, (const u8g_fntpgm_uint8_t*)fnt_default); | ||||
|  | ||||
|   TRACE("return %d", data.adv); | ||||
|   return data.adv; | ||||
| } | ||||
|  | ||||
| static int fontgroup_cb_draw_u8gstrlen(void *userdata, const font_t *fnt_current, const char *msg) { | ||||
|   struct _uxg_drawu8_data_t * pdata = (_uxg_drawu8_data_t*)userdata; | ||||
|  | ||||
|   FU_ASSERT(NULL != userdata); | ||||
|   if (pdata->fnt_prev != fnt_current) { | ||||
|     u8g_SetFont(pdata->pu8g, (const u8g_fntpgm_uint8_t*)fnt_current); | ||||
|     u8g_SetFontPosBottom(pdata->pu8g); | ||||
| @@ -332,15 +281,12 @@ int uxg_GetUtf8StrPixelWidth(u8g_t *pu8g, const char *utf8_msg) { | ||||
|   font_group_t *group = &g_fontgroup_root; | ||||
|   const font_t *fnt_default = uxg_GetFont(pu8g); | ||||
|  | ||||
|   if (!uxg_Utf8FontIsInited()) { | ||||
|     TRACE("Err: utf8 font not initialized."); | ||||
|     return -1; | ||||
|   } | ||||
|   if (!uxg_Utf8FontIsInited()) return -1; | ||||
|  | ||||
|   memset(&data, 0, sizeof(data)); | ||||
|   data.pu8g = pu8g; | ||||
|   data.adv = 0; | ||||
|   fontgroup_drawstring(group, fnt_default, utf8_msg, strlen(utf8_msg), read_byte_ram, (void*)&data, fontgroup_cb_draw_u8gstrlen); | ||||
|   fontgroup_drawstring(group, fnt_default, utf8_msg, read_byte_ram, (void*)&data, fontgroup_cb_draw_u8gstrlen); | ||||
|   u8g_SetFont(pu8g, (const u8g_fntpgm_uint8_t*)fnt_default); | ||||
|  | ||||
|   return data.adv; | ||||
| @@ -361,14 +307,12 @@ int uxg_GetUtf8StrPixelWidthP(u8g_t *pu8g, const char *utf8_msg) { | ||||
|   font_group_t *group = &g_fontgroup_root; | ||||
|   const font_t *fnt_default = uxg_GetFont(pu8g); | ||||
|  | ||||
|   if (!uxg_Utf8FontIsInited()) { | ||||
|     TRACE("Err: utf8 font not initialized."); | ||||
|     return -1; | ||||
|   } | ||||
|   if (!uxg_Utf8FontIsInited()) return -1; | ||||
|  | ||||
|   memset(&data, 0, sizeof(data)); | ||||
|   data.pu8g = pu8g; | ||||
|   data.adv = 0; | ||||
|   fontgroup_drawstring(group, fnt_default, utf8_msg, my_strlen_P(utf8_msg), read_byte_rom, (void*)&data, fontgroup_cb_draw_u8gstrlen); | ||||
|   fontgroup_drawstring(group, fnt_default, utf8_msg, read_byte_rom, (void*)&data, fontgroup_cb_draw_u8gstrlen); | ||||
|   u8g_SetFont(pu8g, (const u8g_fntpgm_uint8_t*)fnt_default); | ||||
|   return data.adv; | ||||
| } | ||||
|   | ||||
| @@ -12,11 +12,6 @@ | ||||
| #include <U8glib.h> | ||||
| #include "fontutils.h" | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
|  | ||||
| // the macro to indicate a UTF-8 string | ||||
| // You should to save the C/C++ source in UTF-8 encoding! | ||||
| // Once you change your UTF-8 strings, you need to call the script uxggenpages.sh to create the font data file fontutf8-data.h | ||||
| @@ -30,21 +25,16 @@ typedef struct _uxg_fontinfo_t { | ||||
|     const u8g_fntpgm_uint8_t *fntdata; | ||||
| } uxg_fontinfo_t; | ||||
|  | ||||
| extern int uxg_SetUtf8Fonts (const uxg_fontinfo_t * fntinfo, int number); // fntinfo is type of PROGMEM | ||||
| extern char uxg_Utf8FontIsInited(void); | ||||
| int uxg_SetUtf8Fonts (const uxg_fontinfo_t * fntinfo, int number); // fntinfo is type of PROGMEM | ||||
|  | ||||
| extern unsigned int uxg_DrawWchar (u8g_t *pu8g, unsigned int x, unsigned int y, wchar_t ch, pixel_len_t max_length); | ||||
| unsigned int uxg_DrawWchar (u8g_t *pu8g, unsigned int x, unsigned int y, wchar_t ch, pixel_len_t max_length); | ||||
|  | ||||
| extern unsigned int uxg_DrawUtf8Str (u8g_t *pu8g, unsigned int x, unsigned int y, const char *utf8_msg, pixel_len_t max_length); | ||||
| extern unsigned int uxg_DrawUtf8StrP (u8g_t *pu8g, unsigned int x, unsigned int y, const char *utf8_msg, pixel_len_t max_length); | ||||
| unsigned int uxg_DrawUtf8Str (u8g_t *pu8g, unsigned int x, unsigned int y, const char *utf8_msg, pixel_len_t max_length); | ||||
| unsigned int uxg_DrawUtf8StrP (u8g_t *pu8g, unsigned int x, unsigned int y, const char *utf8_msg, pixel_len_t max_length); | ||||
|  | ||||
| extern int uxg_GetUtf8StrPixelWidth(u8g_t *pu8g, const char *utf8_msg); | ||||
| extern int uxg_GetUtf8StrPixelWidthP(u8g_t *pu8g, const char *utf8_msg); | ||||
| int uxg_GetUtf8StrPixelWidth(u8g_t *pu8g, const char *utf8_msg); | ||||
| int uxg_GetUtf8StrPixelWidthP(u8g_t *pu8g, const char *utf8_msg); | ||||
|  | ||||
| #define uxg_GetFont(puxg) ((puxg)->font) | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #endif // _UXG_FONTUTF8_H | ||||
|   | ||||
| @@ -80,7 +80,7 @@ | ||||
|   #else | ||||
|     #define MAX_MESSAGE_LENGTH CHARSIZE * 2 * (LCD_WIDTH) | ||||
|   #endif | ||||
|   uint8_t status_scroll_pos = 0; | ||||
|   uint8_t status_scroll_offset = 0; | ||||
| #else | ||||
|   #define MAX_MESSAGE_LENGTH CHARSIZE * (LCD_WIDTH) | ||||
| #endif | ||||
| @@ -5360,30 +5360,8 @@ void lcd_update() { | ||||
|   } // ELAPSED(ms, next_lcd_update_ms) | ||||
| } | ||||
|  | ||||
| inline void pad_message_string() { | ||||
|   uint8_t i = 0, j = 0; | ||||
|   char c; | ||||
|   lcd_status_message[MAX_MESSAGE_LENGTH] = '\0'; | ||||
|   while ((c = lcd_status_message[i]) && j < LCD_WIDTH) { | ||||
|     if (PRINTABLE(c)) j++; | ||||
|     i++; | ||||
|   } | ||||
|   if (true | ||||
|     #if ENABLED(STATUS_MESSAGE_SCROLLING) | ||||
|       && j < LCD_WIDTH | ||||
|     #endif | ||||
|   ) { | ||||
|     // pad with spaces to fill up the line | ||||
|     while (j++ < LCD_WIDTH) lcd_status_message[i++] = ' '; | ||||
|     // chop off at the edge | ||||
|     lcd_status_message[i] = '\0'; | ||||
|   } | ||||
| } | ||||
|  | ||||
| void lcd_finishstatus(const bool persist=false) { | ||||
|  | ||||
|   pad_message_string(); | ||||
|  | ||||
|   #if !(ENABLED(LCD_PROGRESS_BAR) && (PROGRESS_MSG_EXPIRE > 0)) | ||||
|     UNUSED(persist); | ||||
|   #endif | ||||
| @@ -5401,7 +5379,7 @@ void lcd_finishstatus(const bool persist=false) { | ||||
|   #endif | ||||
|  | ||||
|   #if ENABLED(STATUS_MESSAGE_SCROLLING) | ||||
|     status_scroll_pos = 0; | ||||
|     status_scroll_offset = 0; | ||||
|   #endif | ||||
| } | ||||
|  | ||||
| @@ -5413,7 +5391,26 @@ bool lcd_hasstatus() { return (lcd_status_message[0] != '\0'); } | ||||
|  | ||||
| void lcd_setstatus(const char * const message, const bool persist) { | ||||
|   if (lcd_status_message_level > 0) return; | ||||
|   strncpy(lcd_status_message, message, MAX_MESSAGE_LENGTH); | ||||
|  | ||||
|   // Here we have a problem. The message is encoded in UTF8, so | ||||
|   // arbitrarily cutting it will be a problem. We MUST be sure | ||||
|   // that there is no cutting in the middle of a multibyte character! | ||||
|  | ||||
|   // Get a pointer to the null terminator | ||||
|   const char* pend = message + strlen(message); | ||||
|  | ||||
|   //  If length of supplied UTF8 string is greater than | ||||
|   // our buffer size, start cutting whole UTF8 chars | ||||
|   while ((pend - message) > MAX_MESSAGE_LENGTH) { | ||||
|     --pend; | ||||
|     while (!START_OF_UTF8_CHAR(*pend)) --pend; | ||||
|   }; | ||||
|  | ||||
|   // At this point, we have the proper cut point. Use it | ||||
|   uint8_t maxLen = pend - message; | ||||
|   strncpy(lcd_status_message, message, maxLen); | ||||
|   lcd_status_message[maxLen] = '\0'; | ||||
|  | ||||
|   lcd_finishstatus(persist); | ||||
| } | ||||
|  | ||||
| @@ -5421,7 +5418,26 @@ void lcd_setstatusPGM(const char * const message, int8_t level) { | ||||
|   if (level < 0) level = lcd_status_message_level = 0; | ||||
|   if (level < lcd_status_message_level) return; | ||||
|   lcd_status_message_level = level; | ||||
|   strncpy_P(lcd_status_message, message, MAX_MESSAGE_LENGTH); | ||||
|  | ||||
|   // Here we have a problem. The message is encoded in UTF8, so | ||||
|   // arbitrarily cutting it will be a problem. We MUST be sure | ||||
|   // that there is no cutting in the middle of a multibyte character! | ||||
|  | ||||
|   // Get a pointer to the null terminator | ||||
|   const char* pend = message + strlen_P(message); | ||||
|  | ||||
|   //  If length of supplied UTF8 string is greater than | ||||
|   // our buffer size, start cutting whole UTF8 chars | ||||
|   while ((pend - message) > MAX_MESSAGE_LENGTH) { | ||||
|     --pend; | ||||
|     while (!START_OF_UTF8_CHAR(pgm_read_byte(pend))) --pend; | ||||
|   }; | ||||
|  | ||||
|   // At this point, we have the proper cut point. Use it | ||||
|   uint8_t maxLen = pend - message; | ||||
|   strncpy_P(lcd_status_message, message, maxLen); | ||||
|   lcd_status_message[maxLen] = '\0'; | ||||
|  | ||||
|   lcd_finishstatus(level > 0); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -341,9 +341,9 @@ void lcd_kill_screen() { | ||||
|     lcd_moveto(0, h4 * 1); | ||||
|     lcd_put_u8str(lcd_status_message); | ||||
|     lcd_moveto(0, h4 * 2); | ||||
|     lcd_put_u8str_rom(PSTR(MSG_HALTED)); | ||||
|     lcd_put_u8str_P(PSTR(MSG_HALTED)); | ||||
|     lcd_moveto(0, h4 * 3); | ||||
|     lcd_put_u8str_rom(PSTR(MSG_PLEASE_RESET)); | ||||
|     lcd_put_u8str_P(PSTR(MSG_PLEASE_RESET)); | ||||
|   } while (u8g.nextPage()); | ||||
| } | ||||
|  | ||||
| @@ -415,7 +415,7 @@ void lcd_implementation_clear() { } // Automatically cleared by Picture Loop | ||||
|         int8_t pad = (LCD_WIDTH - utf8_strlen_P(pstr)) / 2; | ||||
|         while (--pad >= 0) { lcd_put_wchar(' '); n--; } | ||||
|       } | ||||
|       n -= lcd_put_u8str_max_rom(pstr, n); | ||||
|       n -= lcd_put_u8str_max_P(pstr, n); | ||||
|       if (NULL != valstr) { | ||||
|         n -= lcd_put_u8str_max(valstr, n); | ||||
|       } | ||||
| @@ -431,7 +431,7 @@ void lcd_implementation_clear() { } // Automatically cleared by Picture Loop | ||||
|     if (lcd_implementation_mark_as_selected(row, isSelected)) { | ||||
|       uint8_t n = LCD_WIDTH - (START_COL) - 2; | ||||
|       n *= DOG_CHAR_WIDTH; | ||||
|       n -= lcd_put_u8str_max_rom(pstr, n); | ||||
|       n -= lcd_put_u8str_max_P(pstr, n); | ||||
|       while (n - DOG_CHAR_WIDTH > 0) { n -= lcd_put_wchar(' '); } | ||||
|       lcd_moveto(LCD_PIXEL_WIDTH - (DOG_CHAR_WIDTH), row_y2); | ||||
|       lcd_put_wchar(post_char); | ||||
| @@ -451,11 +451,11 @@ void lcd_implementation_clear() { } // Automatically cleared by Picture Loop | ||||
|       const uint8_t vallen = (pgm ? utf8_strlen_P(data) : utf8_strlen((char*)data)); | ||||
|       uint8_t n = LCD_WIDTH - (START_COL) - 2 - vallen; | ||||
|       n *= DOG_CHAR_WIDTH; | ||||
|       n -= lcd_put_u8str_max_rom(pstr, n); | ||||
|       n -= lcd_put_u8str_max_P(pstr, n); | ||||
|       lcd_put_wchar(':'); | ||||
|       while (n - DOG_CHAR_WIDTH > 0) { n -= lcd_put_wchar(' '); } | ||||
|       lcd_moveto(LCD_PIXEL_WIDTH - (DOG_CHAR_WIDTH) * vallen, row_y2); | ||||
|       if (pgm) lcd_put_u8str_rom(data); else lcd_put_u8str((char*)data); | ||||
|       if (pgm) lcd_put_u8str_P(data); else lcd_put_u8str((char*)data); | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -499,7 +499,7 @@ void lcd_implementation_clear() { } // Automatically cleared by Picture Loop | ||||
|     bool onpage = PAGE_CONTAINS(baseline + 1 - (DOG_CHAR_HEIGHT_EDIT), baseline); | ||||
|     if (onpage) { | ||||
|       lcd_moveto(0, baseline); | ||||
|       lcd_put_u8str_rom(pstr); | ||||
|       lcd_put_u8str_P(pstr); | ||||
|     } | ||||
|  | ||||
|     if (value != NULL) { | ||||
| @@ -641,7 +641,7 @@ void lcd_implementation_clear() { } // Automatically cleared by Picture Loop | ||||
|         if (!isnan(ubl.z_values[x_plot][y_plot])) | ||||
|           lcd_put_u8str(ftostr43sign(ubl.z_values[x_plot][y_plot])); | ||||
|         else | ||||
|           lcd_put_u8str_rom(PSTR(" -----")); | ||||
|           lcd_put_u8str_P(PSTR(" -----")); | ||||
|       } | ||||
|  | ||||
|     } | ||||
|   | ||||
| @@ -347,26 +347,51 @@ void lcd_implementation_clear() { lcd.clear(); } | ||||
|   } | ||||
|  | ||||
|   // Scroll the PSTR 'text' in a 'len' wide field for 'time' milliseconds at position col,line | ||||
|   void lcd_scroll(const int16_t col, const int16_t line, const char* const text, const int16_t len, const int16_t time) { | ||||
|     #if 1 | ||||
|       lcd_put_u8str(text); | ||||
|     #else | ||||
|       char tmp[LCD_WIDTH + 1] = {0}; | ||||
|       int16_t n = MAX(utf8_strlen_P(text) - len, 0); | ||||
|       for (int16_t i = 0; i <= n; i++) { | ||||
|         utf8_strncpy_p(tmp, text + i, MIN(len, LCD_WIDTH)); | ||||
|         lcd_moveto(col, line); | ||||
|         lcd_put_u8str(tmp); | ||||
|         delay(time / MAX(n, 1)); | ||||
|   void lcd_scroll(const uint8_t col, const uint8_t line, const char* const text, const uint8_t len, const int16_t time) { | ||||
|     uint8_t slen = utf8_strlen_P(text); | ||||
|     if (slen < len) { | ||||
|       // Fits into, | ||||
|       lcd_moveto(col, line); | ||||
|       lcd_put_u8str_max_P(text, len); | ||||
|       while (slen < len) { | ||||
|         lcd_put_wchar(' '); | ||||
|         ++slen; | ||||
|       } | ||||
|     #endif | ||||
|       safe_delay(time); | ||||
|     } | ||||
|     else { | ||||
|       const char* p = text; | ||||
|       int dly = time / MAX(slen, 1); | ||||
|       for (uint8_t i = 0; i <= slen; i++) { | ||||
|  | ||||
|         // Go to the correct place | ||||
|         lcd_moveto(col, line); | ||||
|  | ||||
|         // Print the text | ||||
|         lcd_put_u8str_max_P(p, len); | ||||
|  | ||||
|         // Fill with spaces | ||||
|         uint8_t ix = slen - i; | ||||
|         while (ix < len) { | ||||
|           lcd_put_wchar(' '); | ||||
|           ++ix; | ||||
|         } | ||||
|  | ||||
|         // Delay | ||||
|         safe_delay(dly); | ||||
|  | ||||
|         // Advance to the next UTF8 valid position | ||||
|         p++; | ||||
|         while (!START_OF_UTF8_CHAR(pgm_read_byte(p))) p++; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   static void logo_lines(const char* const extra) { | ||||
|     int16_t indent = (LCD_WIDTH - 8 - utf8_strlen_P(extra)) / 2; | ||||
|     lcd_moveto(indent, 0); lcd_put_wchar('\x00'); lcd_put_u8str_rom(PSTR( "------" ));  lcd_put_wchar('\x01'); | ||||
|     lcd_moveto(indent, 1);                        lcd_put_u8str_rom(PSTR("|Marlin|"));  lcd_put_u8str_rom(extra); | ||||
|     lcd_moveto(indent, 2); lcd_put_wchar('\x02'); lcd_put_u8str_rom(PSTR( "------" ));  lcd_put_wchar('\x03'); | ||||
|     lcd_moveto(indent, 0); lcd_put_wchar('\x00'); lcd_put_u8str_P(PSTR( "------" ));  lcd_put_wchar('\x01'); | ||||
|     lcd_moveto(indent, 1);                        lcd_put_u8str_P(PSTR("|Marlin|"));  lcd_put_u8str_P(extra); | ||||
|     lcd_moveto(indent, 2); lcd_put_wchar('\x02'); lcd_put_u8str_P(PSTR( "------" ));  lcd_put_wchar('\x03'); | ||||
|   } | ||||
|  | ||||
|   void lcd_bootscreen() { | ||||
| @@ -379,7 +404,7 @@ void lcd_implementation_clear() { lcd.clear(); } | ||||
|       lcd_erase_line(3); \ | ||||
|       if (utf8_strlen(STRING) <= LCD_WIDTH) { \ | ||||
|         lcd_moveto((LCD_WIDTH - utf8_strlen_P(PSTR(STRING))) / 2, 3); \ | ||||
|         lcd_put_u8str_rom(PSTR(STRING)); \ | ||||
|         lcd_put_u8str_P(PSTR(STRING)); \ | ||||
|         safe_delay(DELAY); \ | ||||
|       } \ | ||||
|       else { \ | ||||
| @@ -452,10 +477,10 @@ void lcd_kill_screen() { | ||||
|     lcd_moveto(0, 2); | ||||
|   #else | ||||
|     lcd_moveto(0, 2); | ||||
|     lcd_put_u8str_rom(PSTR(MSG_HALTED)); | ||||
|     lcd_put_u8str_P(PSTR(MSG_HALTED)); | ||||
|     lcd_moveto(0, 3); | ||||
|   #endif | ||||
|   lcd_put_u8str_rom(PSTR(MSG_PLEASE_RESET)); | ||||
|   lcd_put_u8str_P(PSTR(MSG_PLEASE_RESET)); | ||||
| } | ||||
|  | ||||
| // | ||||
| @@ -473,7 +498,7 @@ FORCE_INLINE void _draw_axis_value(const AxisEnum axis, const char *value, const | ||||
|     else { | ||||
|       #if DISABLED(HOME_AFTER_DEACTIVATE) && DISABLED(DISABLE_REDUCED_ACCURACY_WARNING) | ||||
|         if (!axis_known_position[axis]) | ||||
|           lcd_put_u8str_rom(axis == Z_AXIS ? PSTR("      ") : PSTR("    ")); | ||||
|           lcd_put_u8str_P(axis == Z_AXIS ? PSTR("      ") : PSTR("    ")); | ||||
|         else | ||||
|       #endif | ||||
|           lcd_put_u8str(value); | ||||
| @@ -634,11 +659,11 @@ static void lcd_implementation_status_screen() { | ||||
|  | ||||
|       #if ENABLED(SDSUPPORT) | ||||
|         lcd_moveto(0, 2); | ||||
|         lcd_put_u8str_rom(PSTR("SD")); | ||||
|         lcd_put_u8str_P(PSTR("SD")); | ||||
|         if (IS_SD_PRINTING) | ||||
|           lcd_put_u8str(itostr3(card.percentDone())); | ||||
|         else | ||||
|           lcd_put_u8str_rom(PSTR("---")); | ||||
|           lcd_put_u8str_P(PSTR("---")); | ||||
|           lcd_put_wchar('%'); | ||||
|       #endif // SDSUPPORT | ||||
|  | ||||
| @@ -698,11 +723,11 @@ static void lcd_implementation_status_screen() { | ||||
|     #if LCD_WIDTH >= 20 && ENABLED(SDSUPPORT) | ||||
|  | ||||
|       lcd_moveto(7, 2); | ||||
|       lcd_put_u8str_rom(PSTR("SD")); | ||||
|       lcd_put_u8str_P(PSTR("SD")); | ||||
|       if (IS_SD_PRINTING) | ||||
|         lcd_put_u8str(itostr3(card.percentDone())); | ||||
|       else | ||||
|         lcd_put_u8str_rom(PSTR("---")); | ||||
|         lcd_put_u8str_P(PSTR("---")); | ||||
|       lcd_put_wchar('%'); | ||||
|  | ||||
|     #endif // LCD_WIDTH >= 20 && SDSUPPORT | ||||
| @@ -739,9 +764,9 @@ static void lcd_implementation_status_screen() { | ||||
|     // Show Filament Diameter and Volumetric Multiplier % | ||||
|     // After allowing lcd_status_message to show for 5 seconds | ||||
|     if (ELAPSED(millis(), previous_lcd_status_ms + 5000UL)) { | ||||
|       lcd_put_u8str_rom(PSTR("Dia ")); | ||||
|       lcd_put_u8str_P(PSTR("Dia ")); | ||||
|       lcd_put_u8str(ftostr12ns(filament_width_meas)); | ||||
|       lcd_put_u8str_rom(PSTR(" V")); | ||||
|       lcd_put_u8str_P(PSTR(" V")); | ||||
|       lcd_put_u8str(itostr3(100.0 * ( | ||||
|           parser.volumetric_enabled | ||||
|             ? planner.volumetric_area_nominal / planner.volumetric_multiplier[FILAMENT_SENSOR_EXTRUDER_NUM] | ||||
| @@ -756,35 +781,76 @@ static void lcd_implementation_status_screen() { | ||||
|  | ||||
|   #if ENABLED(STATUS_MESSAGE_SCROLLING) | ||||
|     static bool last_blink = false; | ||||
|     const uint8_t slen = utf8_strlen(lcd_status_message); | ||||
|     const char *stat = lcd_status_message + status_scroll_pos; | ||||
|     if (slen <= LCD_WIDTH) | ||||
|       lcd_put_u8str(stat);                                        // The string isn't scrolling | ||||
|  | ||||
|     // Get the UTF8 character count of the string | ||||
|     uint8_t slen = utf8_strlen(lcd_status_message); | ||||
|  | ||||
|     // If the string fits into the LCD, just print it and do not scroll it | ||||
|     if (slen <= LCD_WIDTH) { | ||||
|  | ||||
|       // The string isn't scrolling and may not fill the screen | ||||
|       lcd_put_u8str(lcd_status_message); | ||||
|  | ||||
|       // Fill the rest with spaces | ||||
|       while (slen < LCD_WIDTH) { | ||||
|         lcd_put_wchar(' '); | ||||
|         ++slen; | ||||
|       } | ||||
|     } | ||||
|     else { | ||||
|       if (status_scroll_pos <= slen - LCD_WIDTH) | ||||
|         lcd_put_u8str(stat);                                      // The string fills the screen | ||||
|       // String is larger than the available space in screen. | ||||
|  | ||||
|       // Get a pointer to the next valid UTF8 character | ||||
|       const char *stat = lcd_status_message + status_scroll_offset; | ||||
|  | ||||
|       // Get the string remaining length | ||||
|       const uint8_t rlen = utf8_strlen(stat); | ||||
|  | ||||
|       // If we have enough characters to display | ||||
|       if (rlen >= LCD_WIDTH) { | ||||
|         // The remaining string fills the screen - Print it | ||||
|         lcd_put_u8str_max(stat, LCD_WIDTH); | ||||
|       } | ||||
|       else { | ||||
|         uint8_t chars = LCD_WIDTH; | ||||
|         if (status_scroll_pos < slen) {                       // First string still visible | ||||
|           lcd_put_u8str(stat);                                    // The string leaves space | ||||
|           chars -= slen - status_scroll_pos;                  // Amount of space left | ||||
|         } | ||||
|         lcd_put_wchar('.');                                       // Always at 1+ spaces left, draw a dot | ||||
|         if (--chars) { | ||||
|           if (status_scroll_pos < slen + 1)                   // Draw a second dot if there's space | ||||
|             --chars, lcd_put_wchar('.'); | ||||
|           if (chars) lcd_put_u8str_max(lcd_status_message, chars); // Print a second copy of the message | ||||
|  | ||||
|         // The remaining string does not completely fill the screen | ||||
|         lcd_put_u8str_max(stat, LCD_WIDTH);               // The string leaves space | ||||
|         uint8_t chars = LCD_WIDTH - rlen;                 // Amount of space left in characters | ||||
|  | ||||
|         lcd_put_wchar('.');                               // Always at 1+ spaces left, draw a dot | ||||
|         if (--chars) {                                    // Draw a second dot if there's space | ||||
|           lcd_put_wchar('.'); | ||||
|           if (--chars) | ||||
|             lcd_put_u8str_max(lcd_status_message, chars); // Print a second copy of the message | ||||
|         } | ||||
|       } | ||||
|       if (last_blink != blink) { | ||||
|         last_blink = blink; | ||||
|         // Skip any non-printing bytes | ||||
|         if (status_scroll_pos < slen) while (!PRINTABLE(lcd_status_message[status_scroll_pos])) status_scroll_pos++; | ||||
|         if (++status_scroll_pos >= slen + 2) status_scroll_pos = 0; | ||||
|  | ||||
|         // Adjust by complete UTF8 characters | ||||
|         if (status_scroll_offset < slen) { | ||||
|           status_scroll_offset++; | ||||
|           while (!START_OF_UTF8_CHAR(lcd_status_message[status_scroll_offset])) | ||||
|             status_scroll_offset++; | ||||
|         } | ||||
|         else | ||||
|           status_scroll_offset = 0; | ||||
|       } | ||||
|     } | ||||
|   #else | ||||
|     lcd_put_u8str(lcd_status_message); | ||||
|     UNUSED(blink); | ||||
|  | ||||
|     // Get the UTF8 character count of the string | ||||
|     uint8_t slen = utf8_strlen(lcd_status_message); | ||||
|  | ||||
|     // Just print the string to the LCD | ||||
|     lcd_put_u8str_max(lcd_status_message, LCD_WIDTH); | ||||
|  | ||||
|     // Fill the rest with spaces if there are missing spaces | ||||
|     while (slen < LCD_WIDTH) { | ||||
|       lcd_put_wchar(' '); | ||||
|       ++slen; | ||||
|     } | ||||
|   #endif | ||||
| } | ||||
|  | ||||
| @@ -809,7 +875,7 @@ static void lcd_implementation_status_screen() { | ||||
|       int8_t pad = (LCD_WIDTH - utf8_strlen_P(pstr)) / 2; | ||||
|       while (--pad >= 0) { lcd_put_wchar(' '); n--; } | ||||
|     } | ||||
|     n -= lcd_put_u8str_max_rom(pstr, n); | ||||
|     n -= lcd_put_u8str_max_P(pstr, n); | ||||
|     if (valstr) n -= lcd_put_u8str_max(valstr, n); | ||||
|     for (; n > 0; --n) lcd_put_wchar(' '); | ||||
|   } | ||||
| @@ -818,7 +884,7 @@ static void lcd_implementation_status_screen() { | ||||
|     uint8_t n = LCD_WIDTH - 2; | ||||
|     lcd_moveto(0, row); | ||||
|     lcd_put_wchar(sel ? pre_char : ' '); | ||||
|     n -= lcd_put_u8str_max_rom(pstr, n); | ||||
|     n -= lcd_put_u8str_max_P(pstr, n); | ||||
|     while (n--) lcd_put_wchar(' '); | ||||
|     lcd_put_wchar(post_char); | ||||
|   } | ||||
| @@ -827,7 +893,7 @@ static void lcd_implementation_status_screen() { | ||||
|     uint8_t n = LCD_WIDTH - 2 - utf8_strlen(data); | ||||
|     lcd_moveto(0, row); | ||||
|     lcd_put_wchar(sel ? pre_char : ' '); | ||||
|     n -= lcd_put_u8str_max_rom(pstr, n); | ||||
|     n -= lcd_put_u8str_max_P(pstr, n); | ||||
|     lcd_put_wchar(':'); | ||||
|     while (n--) lcd_put_wchar(' '); | ||||
|     lcd_put_u8str(data); | ||||
| @@ -836,10 +902,10 @@ static void lcd_implementation_status_screen() { | ||||
|     uint8_t n = LCD_WIDTH - 2 - utf8_strlen_P(data); | ||||
|     lcd_moveto(0, row); | ||||
|     lcd_put_wchar(sel ? pre_char : ' '); | ||||
|     n -= lcd_put_u8str_max_rom(pstr, n); | ||||
|     n -= lcd_put_u8str_max_P(pstr, n); | ||||
|     lcd_put_wchar(':'); | ||||
|     while (n--) lcd_put_wchar(' '); | ||||
|     lcd_put_u8str_rom(data); | ||||
|     lcd_put_u8str_P(data); | ||||
|   } | ||||
|  | ||||
|   #define DRAWMENU_SETTING_EDIT_GENERIC(_src) lcd_implementation_drawmenu_setting_edit_generic(sel, row, pstr, '>', _src) | ||||
| @@ -847,7 +913,7 @@ static void lcd_implementation_status_screen() { | ||||
|  | ||||
|   void lcd_implementation_drawedit(const char* pstr, const char* const value=NULL) { | ||||
|     lcd_moveto(1, 1); | ||||
|     lcd_put_u8str_rom(pstr); | ||||
|     lcd_put_u8str_P(pstr); | ||||
|     if (value != NULL) { | ||||
|       lcd_put_wchar(':'); | ||||
|       int len = utf8_strlen(value); | ||||
| @@ -1293,7 +1359,7 @@ static void lcd_implementation_status_screen() { | ||||
|         if (!isnan(ubl.z_values[x][inverted_y])) | ||||
|           lcd_put_u8str(ftostr43sign(ubl.z_values[x][inverted_y])); | ||||
|         else | ||||
|           lcd_put_u8str_rom(PSTR(" -----")); | ||||
|           lcd_put_u8str_P(PSTR(" -----")); | ||||
|  | ||||
|       #else                 // 16x4 or 20x4 display | ||||
|  | ||||
| @@ -1312,7 +1378,7 @@ static void lcd_implementation_status_screen() { | ||||
|         if (!isnan(ubl.z_values[x][inverted_y])) | ||||
|           lcd_put_u8str(ftostr43sign(ubl.z_values[x][inverted_y])); | ||||
|         else | ||||
|           lcd_put_u8str_rom(PSTR(" -----")); | ||||
|           lcd_put_u8str_P(PSTR(" -----")); | ||||
|  | ||||
|       #endif // LCD_HEIGHT > 3 | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user