From 50596e6a2aad6138e379e0554e0fe96bac046ea1 Mon Sep 17 00:00:00 2001 From: Alexander Petrov Date: Wed, 24 Jun 2026 16:46:57 +0300 Subject: [PATCH] =?UTF-8?q?mdview2:=20=D0=BF=D1=80=D0=BE=D0=B3=D1=80=D0=B5?= =?UTF-8?q?=D1=81=D1=81=D0=B8=D0=B2=D0=BD=D0=B0=D1=8F=20=D0=B7=D0=B0=D0=B3?= =?UTF-8?q?=D1=80=D1=83=D0=B7=D0=BA=D0=B0=20(=D0=BF=D0=B5=D1=80=D0=B2?= =?UTF-8?q?=D1=8B=D0=B9=20=D1=8D=D0=BA=D1=80=D0=B0=D0=BD=20=D1=81=D1=80?= =?UTF-8?q?=D0=B0=D0=B7=D1=83,=20=D1=81=D0=BA=D1=80=D0=BE=D0=BB=D0=BB=20?= =?UTF-8?q?=D0=B2=D0=BE=20=D0=B2=D1=80=D0=B5=D0=BC=D1=8F=20=D0=B8=D0=BD?= =?UTF-8?q?=D0=B4=D0=B5=D0=BA=D1=81=D0=B0=D1=86=D0=B8=D0=B8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Препроцессинг 52КБ занимает ~10с; теперь UI не ждёт его завершения. index_lines() кооперативно вызывает progress_tick() (без прерываний — IM2 отложен): - первый экран рисуется, как только готово ≥ VIEW_H строк (~0.3с); - статус-бар показывает растущее число обработанных строк с многоточием ("L 1-30 / 247..."); - ↑↓ PgUp PgDn Home End (=последняя готовая страница) и F1 работают по УЖЕ готовым строкам (drawable_lines = [0..n_lines-2], т.к. последний сегмент ещё в g_cells до flush); - Esc/F10 во время загрузки — корректный выход (прерывание индексации флагом g_abort + штатный unload_file/pal_reset/clrscr). После каждого тика форсируем ре-маппинг W3 (cur_page=0xFF), т.к. отрисовка/WINREST/BIOS могли сбить страницу, на которую опирается fb(). Временно: debug-индикатор времени обработки "t=Ns" в зоне имени файла (по просьбе — следить за временем при дальнейших оптимизациях). Co-Authored-By: Claude Opus 4.8 --- examples/mdview2/mdview2.c | 129 +++++++++++++++++++++++++++++++++---- 1 file changed, 116 insertions(+), 13 deletions(-) diff --git a/examples/mdview2/mdview2.c b/examples/mdview2/mdview2.c index 339ad76..558ec49 100644 --- a/examples/mdview2/mdview2.c +++ b/examples/mdview2/mdview2.c @@ -30,6 +30,7 @@ #include #include #include +#include /* DEBUG: время обработки в статус-баре прогресса */ /* ---- Геометрия экрана -------------------------------------------- */ @@ -282,6 +283,15 @@ static uint8_t cur_page = 0xFF; /* 0xFF = в W3 ещё не отображ static uint8_t viewport_x = 0; /* горизонтальный сдвиг для nowrap-строк */ static char filename[64] = {0}; +/* Прогрессивная загрузка: рисуем первый экран и пускаем скролл, пока + * index_lines() ещё досканирует файл (кооперативно, без прерываний). */ +static uint8_t g_loading = 0; /* 1 во время index_lines() */ +static uint8_t g_first_drawn = 0; /* первый экран уже нарисован */ +static uint8_t g_abort = 0; /* F10/Esc во время загрузки → прервать индексацию */ +static uint16_t g_last_prog = 0; /* n_lines на последнем обновлении статуса */ +static datetime_t g_load_t0; /* DEBUG: старт обработки (для времени в статусе) */ +static void progress_tick(void); /* определена ниже (нужна в index_lines) */ + #define HPAN_STEP 8u /* Спиннер в статус-баре (col=8,row=0). @@ -1291,10 +1301,11 @@ static void index_lines(void) if (file_size == 0) return; uint32_t p = 0; - while (p < file_size && n_lines < max_lines) { + while (p < file_size && n_lines < max_lines && !g_abort) { - - if ((n_lines & 15) == 0) + if (g_loading) + progress_tick(); /* первый экран + статус + скролл по готовым данным */ + else if ((n_lines & 15) == 0) spinner_tick(); /* Пустая строка — разделитель параграфов. @@ -1562,14 +1573,23 @@ static void draw_viewport_from_cache(void) } } -/* Возвращает процент прокрутки (0..100) относительно доступного диапазона. */ -static uint8_t calc_pct(void) +/* Число строк, готовых к показу. Во время загрузки последняя эмитированная + * строка ещё в g_cells (не зафлашена), поэтому доступны [0..n_lines-2]. */ +static uint16_t drawable_lines(void) { + if (g_loading) return (n_lines >= 1) ? (uint16_t)(n_lines - 1) : 0; + return n_lines; +} + +/* Возвращает процент прокрутки (0..100) относительно доступного диапазона. */ +static uint8_t calc_pct(void) +{ + uint16_t total = drawable_lines(); uint8_t pct; - if (n_lines <= VIEW_H) + if (total <= VIEW_H) pct = 100; - else - pct = (uint8_t)((uint32_t)top_line * 100UL / (uint32_t)(n_lines - VIEW_H)); + else + pct = (uint8_t)((uint32_t)top_line * 100UL / (uint32_t)(total - VIEW_H)); return pct; } @@ -1577,8 +1597,9 @@ static uint8_t calc_pct(void) * Полностью перезаписывает колонки 45..79, чтобы не оставлять «хвосты». */ static void render_status_numbers(void) { - uint16_t last = top_line + VIEW_H; - if (last > n_lines) last = n_lines; + uint16_t total = drawable_lines(); + uint16_t last = top_line + VIEW_H; + if (last > total) last = total; uint8_t pct = calc_pct(); textattr(ATTR_BAR); @@ -1592,13 +1613,25 @@ static void render_status_numbers(void) cputs("-"); dec16(last); cputs(" / "); - dec16(n_lines); - + dec16(total); + if (g_loading) cputs("..."); /* ещё грузится */ wrchar(70, 0, 0xB3, ATTR_BAR); /* вертикальный разделитель */ gotoxy( 73, 0); dec8(pct); cputs("%"); + + if (g_loading) { /* DEBUG: время обработки (зона имени файла) */ + datetime_t now; + getdatetime(&now); + int16_t el = (int16_t)now.minute*60 + now.second + - ((int16_t)g_load_t0.minute*60 + g_load_t0.second); + if (el < 0) el += 3600; + gotoxy(31, 0); + cputs("t="); + dec16((uint16_t)el); + cputs("s "); /* трейлинг-пробелы затирают прошлую ширину */ + } } /* Полностью перерисовывает статус-бар (заголовок + числа). */ @@ -1813,6 +1846,66 @@ static void show_help(void) render_menu(); } +/* Обработка клавиши во время прогрессивной загрузки: скролл строго в + * пределах готовых строк (drawable_lines); End = последняя готовая страница; + * F1 = справка (show_help сам блокирует и перерисовывает). */ +static void load_key(uint16_t k) +{ + uint8_t ascii = (uint8_t)(k & 0xFF); + uint8_t scan = (uint8_t)((k >> 8) & 0x7F); + uint16_t dr = drawable_lines(); + uint16_t maxtop = (dr > VIEW_H) ? (uint16_t)(dr - VIEW_H) : 0; + uint16_t nt = top_line; + + if (ascii == 0x1B || scan == KEY_F10) { g_abort = 1; return; } /* Esc/F10 → выход */ + if (ascii) return; /* прочие ascii игнорируем */ + + switch (scan) { + case KEY_UP: nt = (top_line >= 1) ? (uint16_t)(top_line - 1) : 0; break; + case KEY_DOWN: nt = top_line + 1; break; + case KEY_PGUP: nt = (top_line >= VIEW_H) ? (uint16_t)(top_line - VIEW_H) : 0; break; + case KEY_PGDN: nt = top_line + VIEW_H; break; + case KEY_HOME: top_line = 0; viewport_x = 0; draw_viewport_from_cache(); render_status_numbers(); return; + case KEY_END: top_line = maxtop; viewport_x = 0; draw_viewport_from_cache(); render_status_numbers(); return; + case KEY_LEFT: scroll_h(-(int8_t)HPAN_STEP); render_status_numbers(); return; + case KEY_RIGHT: scroll_h(+(int8_t)HPAN_STEP); render_status_numbers(); return; + case KEY_F1: show_help(); return; + default: return; + } + if (nt > maxtop) nt = maxtop; + if (nt != top_line) { + top_line = nt; + draw_viewport_from_cache(); + render_status_numbers(); + } +} + +/* Кооперативный «тик» прогрессивной загрузки — вызывается из index_lines(). */ +static void progress_tick(void) +{ + uint8_t touched = 0; + + if (!g_first_drawn && drawable_lines() >= VIEW_H) { + draw_viewport_from_cache(); + render_status_numbers(); + g_first_drawn = 1; + touched = 1; + } + if (n_lines >= (uint16_t)(g_last_prog + 20)) { + g_last_prog = n_lines; + spinner_tick(); + if (g_first_drawn) render_status_numbers(); + touched = 1; + } + if (g_first_drawn && kbhit()) { + load_key(getkey()); + touched = 1; + } + /* Отрисовка/WINREST/BIOS могли сбить маппинг W3, на который опирается fb() + * при индексации → форсируем ре-маппинг страницы файла на следующем fb(). */ + if (touched) cur_page = 0xFF; +} + /* ================================================================== * Точка входа * ================================================================== */ @@ -1876,16 +1969,26 @@ int main(int argc, char **argv) cputs("\n\r\n\r"); return 1; } + /* Прогрессивная загрузка: index_lines() сама рисует первый экран и пускает + * скролл по готовым данным через progress_tick(). */ + g_loading = 1; + g_first_drawn = 0; + g_last_prog = 0; + getdatetime(&g_load_t0); /* DEBUG: старт обработки */ index_lines(); + g_loading = 0; spinner_show(0); + if (g_abort) goto exit_loop; /* F10/Esc во время загрузки — корректный выход */ if (n_lines == 0) { die("mdview: empty file"); unload_file(); return 1; } + /* Финальная отрисовка: все строки готовы, top_line мог уехать при скролле. */ + clamp_top(); render_full_status(); - draw_viewport_from_cache(); /* Фаза 4: рендер уже сделан во время index_lines(), рисуем только из кэша */ + draw_viewport_from_cache(); for (;;) { uint16_t k = getkey();