Touch UI long filenames fixes (#19262)
* Improvements to FTDI DLCache functionality. * Better handling of long file names in Touch UI - Long file names now truncated and shown with ellipsis. - Increased display cache buffer to allow for longer filenames. - Visual error message when display cache is exceeded.
This commit is contained in:
		| @@ -133,6 +133,7 @@ class CLCD { | ||||
|     static void set_brightness (uint8_t brightness); | ||||
|     static uint8_t get_brightness(); | ||||
|     static void host_cmd (unsigned char host_command, unsigned char byte2); | ||||
|     static uint32_t dl_size() {return CLCD::mem_read_32(REG::CMD_DL) & 0x1FFF;} | ||||
|  | ||||
|     static void get_font_metrics (uint8_t font, struct FontMetrics &fm); | ||||
|     static uint16_t get_text_width(const uint8_t font, const char *str); | ||||
|   | ||||
| @@ -32,7 +32,8 @@ | ||||
|  * | ||||
|  * The cache memory begins with a table at | ||||
|  * DL_CACHE_START: each table entry contains | ||||
|  * an address and size for a cached DL slot. | ||||
|  * an address, size and used bytes for a cached | ||||
|  * DL slot. | ||||
|  * | ||||
|  * Immediately following the table is the | ||||
|  * DL_FREE_ADDR, which points to free cache | ||||
| @@ -44,11 +45,14 @@ | ||||
|  * | ||||
|  *  DL_CACHE_START  slot0_addr     4 | ||||
|  *                  slot0_size     4 | ||||
|  *                  slot0_used     4 | ||||
|  *                  slot1_addr     4 | ||||
|  *                  slot1_size     4 | ||||
|  *                  slot1_used     4 | ||||
|  *                      ... | ||||
|  *                  slotN_addr     4 | ||||
|  *                  slotN_size     4 | ||||
|  *                  slotN_used     4 | ||||
|  *  DL_FREE_ADDR    dl_free_ptr    4 | ||||
|  *                  cached data | ||||
|  *                      ... | ||||
| @@ -57,7 +61,7 @@ | ||||
|  */ | ||||
|  | ||||
| #define DL_CACHE_START   MAP::RAM_G_SIZE - 0xFFFF | ||||
| #define DL_FREE_ADDR     DL_CACHE_START + DL_CACHE_SLOTS * 8 | ||||
| #define DL_FREE_ADDR     DL_CACHE_START + DL_CACHE_SLOTS * 12 | ||||
|  | ||||
| using namespace FTDI; | ||||
|  | ||||
| @@ -66,12 +70,12 @@ using namespace FTDI; | ||||
| void DLCache::init() { | ||||
|   CLCD::mem_write_32(DL_FREE_ADDR, DL_FREE_ADDR + 4); | ||||
|   for(uint8_t slot = 0; slot < DL_CACHE_SLOTS; slot++) { | ||||
|     save_slot(slot, 0, 0); | ||||
|     save_slot(slot, 0, 0, 0); | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool DLCache::has_data() { | ||||
|   return dl_size != 0; | ||||
|   return dl_slot_size != 0; | ||||
| } | ||||
|  | ||||
| bool DLCache::wait_until_idle() { | ||||
| @@ -93,12 +97,12 @@ bool DLCache::wait_until_idle() { | ||||
|  * that it can be appended later. The memory is | ||||
|  * dynamically allocated following DL_FREE_ADDR. | ||||
|  * | ||||
|  * If num_bytes is provided, then that many bytes | ||||
|  * If min_bytes is provided, then that many bytes | ||||
|  * will be reserved so that the cache may be re-written | ||||
|  * later with potentially a bigger DL. | ||||
|  */ | ||||
|  | ||||
| bool DLCache::store(uint32_t num_bytes /* = 0*/) { | ||||
| bool DLCache::store(uint32_t min_bytes /* = 0*/) { | ||||
|   CLCD::CommandFifo cmd; | ||||
|  | ||||
|   // Execute any commands already in the FIFO | ||||
| @@ -107,67 +111,67 @@ bool DLCache::store(uint32_t num_bytes /* = 0*/) { | ||||
|     return false; | ||||
|  | ||||
|   // Figure out how long the display list is | ||||
|   uint32_t new_dl_size = CLCD::mem_read_32(REG::CMD_DL) & 0x1FFF; | ||||
|   uint32_t free_space  = 0; | ||||
|   uint32_t dl_alloc    = 0; | ||||
|   const uint32_t dl_size = CLCD::dl_size(); | ||||
|  | ||||
|   if (dl_addr == 0) { | ||||
|   if (dl_slot_addr == 0) { | ||||
|     // If we are allocating new space... | ||||
|     dl_addr     = CLCD::mem_read_32(DL_FREE_ADDR); | ||||
|     free_space  = MAP::RAM_G_SIZE - dl_addr; | ||||
|     dl_alloc    = num_bytes ?: new_dl_size; | ||||
|     dl_size     = new_dl_size; | ||||
|   } else { | ||||
|     // Otherwise, we can only store as much space | ||||
|     // as was previously allocated. | ||||
|     free_space  = num_bytes ?: dl_size; | ||||
|     dl_alloc    = 0; | ||||
|     dl_size     = new_dl_size; | ||||
|     dl_slot_addr = CLCD::mem_read_32(DL_FREE_ADDR); | ||||
|     dl_slot_size = max(dl_size, min_bytes); | ||||
|      | ||||
|     const uint32_t free_space = MAP::RAM_G_SIZE - dl_slot_addr; | ||||
|     if(dl_slot_size <= free_space) { | ||||
|       CLCD::mem_write_32(DL_FREE_ADDR, dl_slot_addr + dl_slot_size); | ||||
|     } else { | ||||
|       dl_slot_addr = 0; | ||||
|       dl_slot_size = 0; | ||||
|       dl_slot_used = 0; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (dl_size > free_space) { | ||||
|   if (dl_size > dl_slot_size) { | ||||
|     // Not enough memory to cache the display list. | ||||
|     #if ENABLED(TOUCH_UI_DEBUG) | ||||
|       SERIAL_ECHO_START(); | ||||
|       SERIAL_ECHOPAIR  ("Not enough space in GRAM to cache display list, free space: ", free_space); | ||||
|       SERIAL_ECHOPAIR  ("Not enough space in GRAM to cache display list, free space: ", dl_slot_size); | ||||
|       SERIAL_ECHOLNPAIR(" Required: ", dl_size); | ||||
|     #endif | ||||
|     dl_slot_used = 0; | ||||
|     save_slot(); | ||||
|     return false; | ||||
|   } else { | ||||
|     #if ENABLED(TOUCH_UI_DEBUG) | ||||
|       SERIAL_ECHO_START(); | ||||
|       SERIAL_ECHOPAIR  ("Saving DL to RAMG cache, bytes: ", dl_size); | ||||
|       SERIAL_ECHOLNPAIR(" Free space: ", free_space); | ||||
|       SERIAL_ECHOPAIR  ("Saving DL to RAMG cache, bytes: ", dl_slot_used); | ||||
|       SERIAL_ECHOLNPAIR(" Free space: ", dl_slot_size); | ||||
|     #endif | ||||
|     cmd.memcpy(dl_addr, MAP::RAM_DL, dl_size); | ||||
|     dl_slot_used = dl_size; | ||||
|     save_slot(); | ||||
|     cmd.memcpy(dl_slot_addr, MAP::RAM_DL, dl_slot_used); | ||||
|     cmd.execute(); | ||||
|     save_slot(dl_slot, dl_addr, dl_size); | ||||
|     if (dl_alloc > 0) { | ||||
|       // If we allocated space dynamically, then adjust dl_free_addr. | ||||
|       CLCD::mem_write_32(DL_FREE_ADDR, dl_addr + dl_alloc); | ||||
|     } | ||||
|     return true; | ||||
|   } | ||||
| } | ||||
|  | ||||
| void DLCache::save_slot(uint8_t dl_slot, uint32_t dl_addr, uint32_t dl_size) { | ||||
|   CLCD::mem_write_32(DL_CACHE_START + dl_slot * 8 + 0, dl_addr); | ||||
|   CLCD::mem_write_32(DL_CACHE_START + dl_slot * 8 + 4, dl_size); | ||||
| void DLCache::save_slot(uint8_t indx, uint32_t addr, uint16_t size, uint16_t used) { | ||||
|   CLCD::mem_write_32(DL_CACHE_START + indx * 12 + 0, addr); | ||||
|   CLCD::mem_write_32(DL_CACHE_START + indx * 12 + 4, size); | ||||
|   CLCD::mem_write_32(DL_CACHE_START + indx * 12 + 8, used); | ||||
| } | ||||
|  | ||||
| void DLCache::load_slot() { | ||||
|   dl_addr  = CLCD::mem_read_32(DL_CACHE_START + dl_slot * 8 + 0); | ||||
|   dl_size  = CLCD::mem_read_32(DL_CACHE_START + dl_slot * 8 + 4); | ||||
| void DLCache::load_slot(uint8_t indx, uint32_t &addr, uint16_t &size, uint16_t &used) { | ||||
|   addr  = CLCD::mem_read_32(DL_CACHE_START + indx * 12 + 0); | ||||
|   size  = CLCD::mem_read_32(DL_CACHE_START + indx * 12 + 4); | ||||
|   used  = CLCD::mem_read_32(DL_CACHE_START + indx * 12 + 8); | ||||
| } | ||||
|  | ||||
| void DLCache::append() { | ||||
|   CLCD::CommandFifo cmd; | ||||
|   cmd.append(dl_addr, dl_size); | ||||
|   cmd.append(dl_slot_addr, dl_slot_used); | ||||
|   #if ENABLED(TOUCH_UI_DEBUG) | ||||
|     cmd.execute(); | ||||
|     wait_until_idle(); | ||||
|     SERIAL_ECHO_START(); | ||||
|     SERIAL_ECHOPAIR  ("Appending to DL from RAMG cache, bytes: ", dl_size); | ||||
|     SERIAL_ECHOPAIR  ("Appending to DL from RAMG cache, bytes: ", dl_slot_used); | ||||
|     SERIAL_ECHOLNPAIR(" REG_CMD_DL: ", CLCD::mem_read_32(REG::CMD_DL)); | ||||
|   #endif | ||||
| } | ||||
|   | ||||
| @@ -44,12 +44,16 @@ class DLCache { | ||||
|     typedef FTDI::ftdi_registers  REG; | ||||
|     typedef FTDI::ftdi_memory_map MAP; | ||||
|  | ||||
|     uint8_t  dl_slot; | ||||
|     uint32_t dl_addr; | ||||
|     uint16_t dl_size; | ||||
|     uint8_t  dl_slot_indx; | ||||
|     uint32_t dl_slot_addr; | ||||
|     uint16_t dl_slot_size; | ||||
|     uint16_t dl_slot_used; | ||||
|  | ||||
|     void load_slot(); | ||||
|     static void save_slot(uint8_t dl_slot, uint32_t dl_addr, uint32_t dl_size); | ||||
|     void load_slot() {load_slot(dl_slot_indx, dl_slot_addr, dl_slot_size, dl_slot_used);} | ||||
|     void save_slot() {save_slot(dl_slot_indx, dl_slot_addr, dl_slot_size, dl_slot_used);} | ||||
|      | ||||
|     static void load_slot(uint8_t indx, uint32_t &addr, uint16_t &size, uint16_t &used); | ||||
|     static void save_slot(uint8_t indx, uint32_t  addr, uint16_t  size, uint16_t  used); | ||||
|  | ||||
|     bool wait_until_idle(); | ||||
|  | ||||
| @@ -57,12 +61,12 @@ class DLCache { | ||||
|     static void init(); | ||||
|  | ||||
|     DLCache(uint8_t slot) { | ||||
|       dl_slot = slot; | ||||
|       dl_slot_indx = slot; | ||||
|       load_slot(); | ||||
|     } | ||||
|  | ||||
|     bool has_data(); | ||||
|     bool store(uint32_t num_bytes = 0); | ||||
|     bool store(uint32_t min_bytes = 0); | ||||
|     void append(); | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -47,4 +47,5 @@ | ||||
|   #include "sound_list.h" | ||||
|   #include "polygon.h" | ||||
|   #include "text_box.h" | ||||
|   #include "text_ellipsis.h" | ||||
| #endif | ||||
|   | ||||
| @@ -173,10 +173,21 @@ class UncachedScreen { | ||||
| template<uint8_t DL_SLOT,uint32_t DL_SIZE = 0> | ||||
| class CachedScreen { | ||||
|   protected: | ||||
|     static void gfxError() { | ||||
|       using namespace FTDI; | ||||
|       CommandProcessor cmd; | ||||
|       cmd.cmd(CMD_DLSTART) | ||||
|          .cmd(CLEAR(true,true,true)) | ||||
|          .font(30) | ||||
|          .text(0, 0, display_width, display_height, F("GFX MEM FULL")); | ||||
|     } | ||||
|  | ||||
|     static bool storeBackground() { | ||||
|       DLCache dlcache(DL_SLOT); | ||||
|       if (!dlcache.store(DL_SIZE)) { | ||||
|         SERIAL_ECHO_MSG("CachedScreen::storeBackground() failed: not enough DL cache space"); | ||||
|         gfxError(); // Try to cache a shorter error message instead. | ||||
|         dlcache.store(DL_SIZE); | ||||
|         return false; | ||||
|       } | ||||
|       return true; | ||||
|   | ||||
| @@ -0,0 +1,80 @@ | ||||
| /********************* | ||||
|  * text_ellipsis.cpp * | ||||
|  *********************/ | ||||
|  | ||||
| /**************************************************************************** | ||||
|  *   Written By Marcio Teixeira 2019 - Aleph Objects, Inc.                  * | ||||
|  *                                                                          * | ||||
|  *   This program is free software: you can redistribute it and/or modify   * | ||||
|  *   it under the terms of the GNU General Public License as published by   * | ||||
|  *   the Free Software Foundation, either version 3 of the License, or      * | ||||
|  *   (at your option) any later version.                                    * | ||||
|  *                                                                          * | ||||
|  *   This program is distributed in the hope that it will be useful,        * | ||||
|  *   but WITHOUT ANY WARRANTY; without even the implied warranty of         * | ||||
|  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          * | ||||
|  *   GNU General Public License for more details.                           * | ||||
|  *                                                                          * | ||||
|  *   To view a copy of the GNU General Public License, go to the following  * | ||||
|  *   location: <https://www.gnu.org/licenses/>.                              * | ||||
|  ****************************************************************************/ | ||||
|  | ||||
| #include "ftdi_extended.h" | ||||
|  | ||||
| #ifdef FTDI_EXTENDED | ||||
|  | ||||
| namespace FTDI { | ||||
|  | ||||
|   /** | ||||
|    * Helper function for drawing text with ellipses. The str buffer may be modified and should have space for up to two extra characters. | ||||
|    */ | ||||
|   static void _draw_text_with_ellipsis(CommandProcessor& cmd, int16_t x, int16_t y, int16_t w, int16_t h, char *str, uint16_t options, uint8_t font) { | ||||
|     FontMetrics fm(font); | ||||
|     const uint16_t ellipsisWidth = fm.get_char_width('.') * 3; | ||||
|  | ||||
|     // Compute the total line length, as well as | ||||
|     // the location in the string where it can | ||||
|     // split and still allow the ellipsis to fit. | ||||
|     uint16_t lineWidth = 0; | ||||
|     char *breakPoint   = str; | ||||
|     for(char* c = str; *c; c++) { | ||||
|       lineWidth += fm.get_char_width(*c); | ||||
|       if(lineWidth + ellipsisWidth < w) | ||||
|         breakPoint = c; | ||||
|     } | ||||
|  | ||||
|     if(lineWidth > w) { | ||||
|       *breakPoint = '\0'; | ||||
|       strcpy_P(breakPoint,PSTR("...")); | ||||
|     } | ||||
|  | ||||
|     cmd.apply_text_alignment(x, y, w, h, options); | ||||
|     #ifdef TOUCH_UI_USE_UTF8 | ||||
|       if (has_utf8_chars(str)) { | ||||
|         draw_utf8_text(cmd, x, y, str, font_size_t::from_romfont(font), options); | ||||
|       } else | ||||
|     #endif | ||||
|       { | ||||
|         cmd.CLCD::CommandFifo::text(x, y, font, options); | ||||
|         cmd.CLCD::CommandFifo::str(str); | ||||
|       } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|     * These functions draws text inside a bounding box, truncating the text and | ||||
|     * adding ellipsis if the text does not fit. | ||||
|     */ | ||||
|   void draw_text_with_ellipsis(CommandProcessor& cmd, int x, int y, int w, int h, const char *str, uint16_t options, uint8_t font) { | ||||
|     char tmp[strlen(str) + 3]; | ||||
|     strcpy(tmp, str); | ||||
|     _draw_text_with_ellipsis(cmd, x, y, w, h, tmp, options, font); | ||||
|   } | ||||
|  | ||||
|   void draw_text_with_ellipsis(CommandProcessor& cmd, int x, int y, int w, int h, progmem_str pstr, uint16_t options, uint8_t font) { | ||||
|     char tmp[strlen_P((const char*)pstr) + 3]; | ||||
|     strcpy_P(tmp, (const char*)pstr); | ||||
|     _draw_text_with_ellipsis(cmd, x, y, w, h, tmp, options, font); | ||||
|   } | ||||
| } // namespace FTDI | ||||
|  | ||||
| #endif // FTDI_EXTENDED | ||||
| @@ -0,0 +1,31 @@ | ||||
| /******************* | ||||
|  * text_ellipsis.h * | ||||
|  *******************/ | ||||
|  | ||||
| /**************************************************************************** | ||||
|  *   Written By Marcio Teixeira 2020 - SynDaver Labs, Inc.                  * | ||||
|  *                                                                          * | ||||
|  *   This program is free software: you can redistribute it and/or modify   * | ||||
|  *   it under the terms of the GNU General Public License as published by   * | ||||
|  *   the Free Software Foundation, either version 3 of the License, or      * | ||||
|  *   (at your option) any later version.                                    * | ||||
|  *                                                                          * | ||||
|  *   This program is distributed in the hope that it will be useful,        * | ||||
|  *   but WITHOUT ANY WARRANTY; without even the implied warranty of         * | ||||
|  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          * | ||||
|  *   GNU General Public License for more details.                           * | ||||
|  *                                                                          * | ||||
|  *   To view a copy of the GNU General Public License, go to the following  * | ||||
|  *   location: <https://www.gnu.org/licenses/>.                              * | ||||
|  ****************************************************************************/ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| /** | ||||
|  * This function draws text inside a bounding box, truncating the text and | ||||
|  * showing ellipsis if it does not fit. | ||||
|  */ | ||||
| namespace FTDI { | ||||
|   void draw_text_with_ellipsis(class CommandProcessor& cmd, int x, int y, int w, int h, progmem_str str, uint16_t options = 0, uint8_t font = 31); | ||||
|   void draw_text_with_ellipsis(class CommandProcessor& cmd, int x, int y, int w, int h, const char *str, uint16_t options = 0, uint8_t font = 31); | ||||
| } | ||||
| @@ -83,15 +83,19 @@ void FilesScreen::drawFileButton(const char* filename, uint8_t tag, bool is_dir, | ||||
|   cmd.font(font_medium) | ||||
|      .rectangle( 0, BTN_Y(header_h+line), display_width, BTN_H(1)); | ||||
|   cmd.cmd(COLOR_RGB(is_highlighted ? normal_btn.rgb : bg_text_enabled)); | ||||
|   constexpr uint16_t dim[2] = {BTN_SIZE(6,1)}; | ||||
|   #define POS_AND_SHORTEN(SHORTEN) BTN_POS(1,header_h+line), dim[0] - (SHORTEN), dim[1] | ||||
|   #define POS_AND_SIZE             POS_AND_SHORTEN(0) | ||||
|   #if ENABLED(SCROLL_LONG_FILENAMES) | ||||
|     if (is_highlighted) { | ||||
|       cmd.cmd(SAVE_CONTEXT()); | ||||
|       cmd.cmd(MACRO(0)); | ||||
|     } | ||||
|       cmd.text(POS_AND_SIZE, filename, OPT_CENTERY | OPT_NOFIT); | ||||
|     } else | ||||
|   #endif | ||||
|   cmd.text  (BTN_POS(1,header_h+line), BTN_SIZE(6,1), filename, OPT_CENTERY | TERN0(SCROLL_LONG_FILENAMES, OPT_NOFIT)); | ||||
|   if (is_dir) { | ||||
|     cmd.text(BTN_POS(1,header_h+line), BTN_SIZE(6,1), F("> "),  OPT_CENTERY | OPT_RIGHTX); | ||||
|   draw_text_with_ellipsis(cmd, POS_AND_SHORTEN(is_dir ? 20 : 0), filename, OPT_CENTERY, font_medium); | ||||
|   if (is_dir && !is_highlighted) { | ||||
|     cmd.text(POS_AND_SIZE, F("> "),  OPT_CENTERY | OPT_RIGHTX); | ||||
|   } | ||||
|   #if ENABLED(SCROLL_LONG_FILENAMES) | ||||
|     if (is_highlighted) { | ||||
| @@ -102,7 +106,7 @@ void FilesScreen::drawFileButton(const char* filename, uint8_t tag, bool is_dir, | ||||
|  | ||||
| void FilesScreen::drawFileList() { | ||||
|   FileList files; | ||||
|   screen_data.FilesScreen.num_page = max(1,(ceil)(float(files.count()) / files_per_page)); | ||||
|   screen_data.FilesScreen.num_page = max(1,ceil(float(files.count()) / files_per_page)); | ||||
|   screen_data.FilesScreen.cur_page = min(screen_data.FilesScreen.cur_page, screen_data.FilesScreen.num_page-1); | ||||
|   screen_data.FilesScreen.flags.is_root  = files.isAtRootDir(); | ||||
|  | ||||
| @@ -134,7 +138,6 @@ void FilesScreen::drawHeader() { | ||||
|   sprintf_P(str, PSTR("Page %d of %d"), | ||||
|     screen_data.FilesScreen.cur_page + 1, screen_data.FilesScreen.num_page); | ||||
|  | ||||
|  | ||||
|   CommandProcessor cmd; | ||||
|   cmd.colors(normal_btn) | ||||
|      .font(font_small) | ||||
|   | ||||
| @@ -96,7 +96,7 @@ enum { | ||||
| #define STATUS_SCREEN_DL_SIZE        2048 | ||||
| #define ALERT_BOX_DL_SIZE            3072 | ||||
| #define SPINNER_DL_SIZE              3072 | ||||
| #define FILE_SCREEN_DL_SIZE          3072 | ||||
| #define FILE_SCREEN_DL_SIZE          4160 | ||||
| #define PRINTING_SCREEN_DL_SIZE      2048 | ||||
|  | ||||
| /************************* MENU SCREEN DECLARATIONS *************************/ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user