mdview2: RAW-просмотр исходника (F2/F3) + инкрементальная UTF-8 конвертация
Новый модуль mdview2_raw.c (+ mdview2.h с общими константами/атрибутами и
extern'ами) — первый шаг разбиения монолита. Опционален через mdview2_conf.h
(#define WITH_RAW): при 0 — пустой объектник, нулевой расход (проверено:
размер как до RAW).
RAW-просмотр (без markdown-форматирования):
- работает по активному буферу (8-бит как есть / UTF-8 декодированный), 1 байт
= 1 ячейка, \t→пробел, ремап CP1251/KOI8 на отрисовке;
- два под-режима: wrap (перенос кратно 80) и hscroll (одна строка + ←/→);
- прокрутка на 1 строку через аппаратный scroll + отрисовка одной строки;
вывод char-буфером (bios_write_until по фону ATTR_TEXT), без win_rest/scratch;
- индекс/кэш markdown не используются, 0 доп. EMM.
Клавиши/меню:
- F2 — тумблер RAW↔MD (запоминает под-режим RAW);
- F3 — Wrap/Unwrap (только в RAW), метка показывает целевой режим;
- меню перестроено: блоки по 8 кол (col i*8), номера всех 10 клавиш без 'F'
(' 1'..' 9','10'), текст-функция 6 симв. сразу за номером и только когда
функция доступна; F8 сокращён до CodePg.
Инкрементальная UTF-8→CP866 конвертация: вместо полного прохода перед
индексацией — чанками впереди позиции чтения (CONV_MARGIN), первый экран
появляется быстро. Конвертер читает оригинал через cv_read (своя W3-страница),
index_lines докручивает конвертацию; progress_tick рисует по текущему виду
(MD/RAW), без мелькания чужого вида.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+204
-96
@@ -30,25 +30,7 @@
|
||||
#include <sprinter.h>
|
||||
#include <sprinter_mem.h>
|
||||
#include <palette.h>
|
||||
|
||||
/* ---- Геометрия экрана -------------------------------------------- */
|
||||
|
||||
#define SCREEN_W 80
|
||||
#define SCREEN_H 32
|
||||
#define VIEW_TOP_ROW 1
|
||||
#define VIEW_H 30 /* видимая область: строки 1..30 включительно */
|
||||
#define MENU_ROW 31
|
||||
#define TAB_STOP 4
|
||||
|
||||
/* ---- Параметры файла и памяти ------------------------------------ */
|
||||
|
||||
#define PAGE_BITS 14u
|
||||
#define PAGE_SIZE (1u << PAGE_BITS) /* размер EMM-страницы: 16 КБ */
|
||||
#define PAGE_MASK ((uint16_t)(PAGE_SIZE - 1u))
|
||||
#define MAX_PAGES 8 /* 8 страниц × 16 КБ = 128 КБ */
|
||||
#define MAX_FILE ((uint32_t)MAX_PAGES * PAGE_SIZE) /* максимальный размер файла: 131072 байт */
|
||||
// #define MAX_LINES 2048
|
||||
#define FILE_BUF ((char *)0xC000) /* окно W3, куда мапится текущая EMM-страница */
|
||||
#include "mdview2.h" /* геометрия, атрибуты, extern для модуля RAW */
|
||||
|
||||
/* ---- Палитра атрибутов ------------------------------------------- */
|
||||
|
||||
@@ -71,33 +53,7 @@ static const uint8_t md_pallete[16 * 4] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
#define ATTR_RESET COLOR(COLOR_LIGHTGRAY, COLOR_BLACK)
|
||||
#define ATTR_TEXT COLOR(COLOR_LIGHTGRAY, COLOR_BLUE)
|
||||
#define ATTR_TEXT_TITLE1 COLOR(COLOR_YELLOW, COLOR_BLUE)
|
||||
#define ATTR_TEXT_TITLE2 COLOR(COLOR_LIGHTBLUE, COLOR_BLUE)
|
||||
#define ATTR_TEXT_TITLE3 COLOR(COLOR_LIGHTGREEN, COLOR_BLUE)
|
||||
#define ATTR_TEXT_TITLE4 COLOR(COLOR_LIGHTGREEN, COLOR_BLUE)
|
||||
#define ATTR_TEXT_BOLD COLOR(COLOR_LIGHTRED, COLOR_BLUE)
|
||||
#define ATTR_TEXT_ITALIC COLOR(COLOR_LIGHTGREEN, COLOR_BLUE)
|
||||
#define ATTR_TEXT_UNDERSORE COLOR(COLOR_LIGHTMAGENTA, COLOR_BLUE)
|
||||
#define ATTR_TEXT_CODE COLOR(COLOR_WHITE, COLOR_BLUE)
|
||||
#define ATTR_TEXT_STRIKE COLOR(COLOR_DARKGRAY, COLOR_BLUE)
|
||||
#define ATTR_LIST_MARKER COLOR(COLOR_LIGHTCYAN, COLOR_BLUE)
|
||||
#define ATTR_QUOTE_MARKER COLOR(COLOR_CYAN, COLOR_BLUE)
|
||||
#define ATTR_HR COLOR(COLOR_CYAN, COLOR_BLUE)
|
||||
#define ATTR_BOX COLOR(COLOR_CYAN, COLOR_BLUE) /* рамка таблиц; отдельный (не контентный) attr — чтобы ремап кодировок не трогал box-глифы */
|
||||
#define ATTR_TRUNC COLOR(COLOR_YELLOW, COLOR_BLUE)
|
||||
#define ATTR_BAR COLOR(COLOR_BLACK, COLOR_LIGHTCYAN)
|
||||
#define ATTR_BAR_SPINNER COLOR(COLOR_WHITE, COLOR_LIGHTCYAN)
|
||||
#define ATTR_MENU_T COLOR(COLOR_BLACK, COLOR_LIGHTCYAN)
|
||||
#define ATTR_MENU_K COLOR(COLOR_YELLOW, COLOR_BLACK)
|
||||
|
||||
/* ---- Атрибуты диалога справки ------------------------------------ */
|
||||
|
||||
#define ATTR_HELP_BG COLOR(COLOR_LIGHTGRAY, COLOR_BLACK)
|
||||
#define ATTR_HELP_BDR COLOR(COLOR_WHITE, COLOR_BLACK)
|
||||
#define ATTR_HELP_TIT COLOR(COLOR_YELLOW, COLOR_BLACK)
|
||||
#define ATTR_HELP_HDR COLOR(COLOR_WHITE, COLOR_BLACK)
|
||||
/* Атрибуты экрана/справки и геометрия — в mdview2.h (общие с модулем RAW). */
|
||||
#define ATTR_HELP_HINT COLOR(COLOR_YELLOW, COLOR_BLACK)
|
||||
|
||||
/* Геометрия диалога справки (в символьных координатах 80×32). */
|
||||
@@ -269,7 +225,7 @@ static uint8_t emph_to_attr(uint8_t emph, uint8_t base_attr);
|
||||
static uint16_t n_lines = 0;
|
||||
static uint16_t max_lines = 0; /* ёмкость индекса: index_pages * 2048 */
|
||||
static uint16_t top_line = 0;
|
||||
static uint32_t file_size = 0;
|
||||
uint32_t file_size = 0;
|
||||
static uint8_t file_blk = 0;
|
||||
static uint8_t file_pages = 0;
|
||||
static uint8_t file_phys[MAX_PAGES] = {0};
|
||||
@@ -285,6 +241,11 @@ static char filename[64] = {0};
|
||||
|
||||
/* Прогрессивная загрузка: рисуем первый экран и пускаем скролл, пока
|
||||
* index_lines() ещё досканирует файл (кооперативно, без прерываний). */
|
||||
#if WITH_RAW
|
||||
uint8_t g_view = VIEW_MD; /* режим просмотра (общий с mdview2_raw.c) */
|
||||
static uint8_t g_ready = 0; /* 1 когда первичный документ построен — F2 (RAW) рабочий */
|
||||
static uint8_t g_raw_last = VIEW_RAW_WRAP; /* последний под-режим RAW (возврат по F2) */
|
||||
#endif
|
||||
static uint8_t g_loading = 0; /* 1 во время index_lines() */
|
||||
static uint8_t g_first_drawn = 0; /* первый экран уже нарисован */
|
||||
static uint8_t g_f8_enabled = 0; /* можно ли сейчас переключать кодировку (метка F8 в меню + F8 в load_key) */
|
||||
@@ -300,8 +261,8 @@ static void progress_tick(void); /* определена ниже (нужн
|
||||
#define ENC_KOI8R 2
|
||||
#define ENC_UTF8 3
|
||||
static uint8_t g_encoding = ENC_CP866; /* активная кодировка */
|
||||
static const uint8_t *g_remap = 0; /* активная таблица ремапа (CP1251/KOI8), 0 = без ремапа */
|
||||
static uint8_t g_scratch_phys = 0; /* EMM-страница под ремап-буфер строки для win_rest */
|
||||
const uint8_t *g_remap = 0; /* активная таблица ремапа (CP1251/KOI8), 0 = без ремапа */
|
||||
uint8_t g_scratch_phys = 0; /* EMM-страница под ремап-буфер строки для win_rest */
|
||||
|
||||
/* UTF-8 набор (Фаза 2): src декодируется в отдельный блок EMM-страниц как
|
||||
* чистый CP866. После конвертации активный буфер файла (file_phys/file_size/
|
||||
@@ -312,7 +273,16 @@ static uint8_t utf_blk = 0;
|
||||
static uint8_t utf_pages = 0;
|
||||
static uint8_t utf_phys[MAX_PAGES] = {0};
|
||||
static uint32_t utf_size = 0;
|
||||
static uint8_t utf_avail = 0; /* 1 = UTF-8 набор удалось сконвертировать/построить */
|
||||
static uint8_t utf_avail = 0; /* 1 = страницы UTF-8 выделены */
|
||||
|
||||
/* Инкрементальная конвертация UTF-8→CP866: идёт чанками, чередуясь с
|
||||
* индексацией (index_lines докручивает конвертацию впереди позиции чтения),
|
||||
* чтобы первый экран появлялся быстро, а не после полного прохода. */
|
||||
static uint8_t g_utf_building = 0; /* 1 пока конвертация не дошла до конца исходника */
|
||||
static uint32_t utf_src = 0; /* позиция чтения ОРИГИНАЛА (utf-8) конвертером */
|
||||
static uint8_t cv_page = 0xFF;/* orig-страница в W3 для cv_read (своя от cur_page) */
|
||||
#define CONV_MARGIN 4096u /* на сколько байт держать конвертацию впереди индексатора */
|
||||
static void utf_convert_more(uint32_t target); /* forward (нужна в index_lines) */
|
||||
|
||||
/* Снимок исходного (8-битного) файлового буфера — набор DOC_8BIT читает его
|
||||
* как есть, набор DOC_UTF8 читает utf_phys. */
|
||||
@@ -386,10 +356,11 @@ static void put_str_attr(uint8_t x, uint8_t y, const char *s, uint8_t attr)
|
||||
|
||||
|
||||
/* Полностью очищает указанную строку экрана символами пробела с attr. */
|
||||
static void fill_row(uint8_t y, uint8_t attr)
|
||||
void fill_row(uint8_t y, uint8_t attr)
|
||||
{
|
||||
bios_set_place(y, 0);
|
||||
bios_fillcharattr(' ', attr, SCREEN_W);
|
||||
// bios_set_place(y, 0);
|
||||
// bios_fillcharattr(' ', attr, SCREEN_W);
|
||||
bios_clearwin(y, 0, 1, SCREEN_W, attr);
|
||||
}
|
||||
|
||||
/* Продвигает спиннер на один кадр.
|
||||
@@ -439,7 +410,7 @@ static inline void map_page(uint8_t page)
|
||||
* bank_read читает через IN — общий аппаратный регистр, без теневой RAM.
|
||||
* NB: __naked обязателен (внутри собственный ret); IX/IY не трогаем
|
||||
* (callee-saved), BC/DE/HL/AF — caller-saved, можно клобберить. */
|
||||
static char fb(uint32_t p) __naked
|
||||
char fb(uint32_t p) __naked
|
||||
{
|
||||
(void)p; /* p = HLDE; доступ только из asm */
|
||||
__asm
|
||||
@@ -497,7 +468,7 @@ static char fb(uint32_t p) __naked
|
||||
* Когда Фаза 2 начала пакетировать много строк на одной странице с
|
||||
* РАЗНЫМИ off, всё стало читать с начала страницы (т.е. контент первой
|
||||
* записи) независимо от реальной строки — добавлен off, IX = 0xC000+off. */
|
||||
static void win_rest(uint8_t row, uint8_t col, uint8_t h, uint8_t w, uint8_t page, uint16_t off) __naked
|
||||
void win_rest(uint8_t row, uint8_t col, uint8_t h, uint8_t w, uint8_t page, uint16_t off) __naked
|
||||
{
|
||||
(void)row; (void)col; (void)h; (void)w; (void)page; (void)off;
|
||||
__asm
|
||||
@@ -586,7 +557,7 @@ static void idx_put(uint16_t idx, const idx_rec_t *r)
|
||||
bank_write(index_phys[page], off, r, INDEX_REC_SIZE);
|
||||
}
|
||||
|
||||
static uint32_t seg_off(uint16_t idx)
|
||||
uint32_t seg_off(uint16_t idx)
|
||||
{
|
||||
idx_rec_t r;
|
||||
if (idx >= n_lines) return file_size;
|
||||
@@ -1433,6 +1404,12 @@ static void index_lines(void)
|
||||
uint32_t p = 0;
|
||||
while (p < file_size && n_lines < max_lines && !g_abort) {
|
||||
|
||||
/* UTF-8: держим конвертацию впереди позиции чтения (включая look-ahead
|
||||
* склейки/классификации ~ один абзац). file_size при этом = доступная
|
||||
* сконвертированная часть и растёт по ходу. */
|
||||
if (g_utf_building && (uint32_t)(p + CONV_MARGIN) > file_size)
|
||||
utf_convert_more((uint32_t)(p + CONV_MARGIN));
|
||||
|
||||
if (g_loading)
|
||||
progress_tick(); /* первый экран + статус + скролл по готовым данным */
|
||||
else if ((n_lines & 15) == 0)
|
||||
@@ -1957,14 +1934,28 @@ static void conv_emit_cp(uint32_t cp)
|
||||
|
||||
/* Декодирует активный (исходный UTF-8) буфер в utf_phys как CP866.
|
||||
* Битые/обрезанные последовательности → '?'; 4-байтные (эмодзи и т.п.) → '?'. */
|
||||
static void utf8_convert(void)
|
||||
/* Последовательное чтение байта ОРИГИНАЛА (utf-8) конвертером. Оригинал лежит
|
||||
* в orig_file_phys[]; мапим его страницу в W3 напрямую, со своей кэш-переменной
|
||||
* cv_page (отдельной от cur_page индексатора, т.к. оба используют W3 и
|
||||
* чередуются). На границе с fb()-чтением utf-буфера кэши взаимно сбрасываются. */
|
||||
static uint8_t cv_read(uint32_t s)
|
||||
{
|
||||
conv_n = 0; conv_page = 0; conv_off = 0;
|
||||
uint32_t n = file_size;
|
||||
uint32_t p = 0;
|
||||
while (p < n) {
|
||||
if ((uint16_t)((uint16_t)p & 0x0FFFu) == 0) spinner_tick();
|
||||
uint8_t b = (uint8_t)fb(p++);
|
||||
uint8_t pg = (uint8_t)(s >> PAGE_BITS);
|
||||
if (pg != cv_page) { sprinter_page_w3(orig_file_phys[pg]); cv_page = pg; }
|
||||
return *((volatile uint8_t *)(0xC000u + (uint16_t)(s & PAGE_MASK)));
|
||||
}
|
||||
|
||||
/* Возобновляемая конвертация: дописывает utf_phys из оригинала, пока
|
||||
* сконвертированных (flushed) байт меньше target и не достигнут конец
|
||||
* исходника. По выходу file_size = доступная (сконвертированная) часть; при
|
||||
* достижении конца фиксирует utf_size и снимает g_utf_building. */
|
||||
static void utf_convert_more(uint32_t target)
|
||||
{
|
||||
cv_page = 0xFF; /* W3 был на utf (fb индексатора) — пере-смаппим orig */
|
||||
uint32_t n = orig_file_size;
|
||||
while (utf_src < n &&
|
||||
(uint32_t)((uint32_t)conv_page * PAGE_SIZE + conv_off) < target) {
|
||||
uint8_t b = (uint8_t)cv_read(utf_src++);
|
||||
if (b < 0x80) { conv_emit_cp(b); continue; }
|
||||
|
||||
uint32_t cp;
|
||||
@@ -1972,21 +1963,23 @@ static void utf8_convert(void)
|
||||
if ((b & 0xE0) == 0xC0) { cp = (uint32_t)(b & 0x1F); need = 1; }
|
||||
else if ((b & 0xF0) == 0xE0) { cp = (uint32_t)(b & 0x0F); need = 2; }
|
||||
else if ((b & 0xF8) == 0xF0) { cp = (uint32_t)(b & 0x07); need = 3; }
|
||||
else { conv_put('?'); continue; } /* одиночный континюэйшн/битый лид */
|
||||
else { conv_put('?'); continue; }
|
||||
|
||||
uint8_t ok = 1;
|
||||
for (uint8_t k = 0; k < need; k++) {
|
||||
if (p >= n) { ok = 0; break; }
|
||||
uint8_t cb = (uint8_t)fb(p);
|
||||
if (utf_src >= n) { ok = 0; break; }
|
||||
uint8_t cb = (uint8_t)cv_read(utf_src);
|
||||
if ((cb & 0xC0) != 0x80) { ok = 0; break; }
|
||||
cp = (cp << 6) | (uint32_t)(cb & 0x3F);
|
||||
p++;
|
||||
utf_src++;
|
||||
}
|
||||
if (!ok || need == 3) { conv_put('?'); continue; } /* битое / 4-байтное */
|
||||
if (!ok || need == 3) { conv_put('?'); continue; }
|
||||
conv_emit_cp(cp);
|
||||
}
|
||||
conv_flush();
|
||||
utf_size = (uint32_t)conv_page * PAGE_SIZE + conv_off;
|
||||
file_size = (uint32_t)conv_page * PAGE_SIZE + conv_off; /* доступно индексатору */
|
||||
if (utf_src >= n) { utf_size = file_size; g_utf_building = 0; }
|
||||
cur_page = 0xFF; /* W3 был на orig — fb индексатора пере-смаппит utf */
|
||||
}
|
||||
|
||||
/* Выделяет страницы под UTF-8 набор и конвертирует туда исходник (читает
|
||||
@@ -1994,15 +1987,16 @@ static void utf8_convert(void)
|
||||
* активный буфер: это делает build_doc() при сборке набора DOC_UTF8.
|
||||
* Конвертированный поток ≤ исходного (кириллица 2 байта → 1), хватает того же
|
||||
* числа страниц. Возврат: 1 — успех, 0 — нет EMM. */
|
||||
static uint8_t alloc_and_convert_utf8(void)
|
||||
/* Выделяет страницы под UTF-8 набор (конвертированный ≤ оригинала по размеру).
|
||||
* Сама конвертация — инкрементальная, через utf_convert_more(). Возврат: 1 —
|
||||
* успех, 0 — нет EMM. */
|
||||
static uint8_t utf_alloc(void)
|
||||
{
|
||||
utf_pages = file_pages;
|
||||
utf_pages = orig_file_pages;
|
||||
utf_blk = mem_alloc_pages(utf_pages);
|
||||
if (utf_blk == 0) return 0;
|
||||
for (uint8_t i = 0; i < utf_pages; i++)
|
||||
utf_phys[i] = mem_get_page(utf_blk, i);
|
||||
|
||||
utf8_convert(); /* читает оригинал (active) → utf_phys */
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -2121,18 +2115,30 @@ static void render_updated_status(void)
|
||||
/* Отрисовывает нижнюю строку меню. */
|
||||
static void render_menu(void)
|
||||
{
|
||||
/* Строка меню разбита на 10 блоков по 8 колонок: метку Fn кладём в блок
|
||||
* (n-1)*8, чтобы позиции соответствовали номерам клавиш (F1→0, F8→7,
|
||||
* F10→9). Пока заполнены только F1/F8/F10. */
|
||||
/* Блок i = колонка i*8 (8 шириной): 2 поз. НОМЕР клавиши без 'F' (стиль
|
||||
* ATTR_MENU_K, ведущий «чёрный» пробел кроме F10) + 6 поз. ТЕКСТ-функция
|
||||
* (ATTR_MENU_T) сразу за номером. Номера рисуем для ВСЕХ 10 клавиш; текст —
|
||||
* только у задействованных и доступных сейчас. */
|
||||
fill_row(MENU_ROW, ATTR_MENU_T);
|
||||
put_str_attr(0 * 8 + 1, MENU_ROW, "F1", ATTR_MENU_K);
|
||||
put_str_attr(0 * 8 + 4, MENU_ROW, "Help", ATTR_MENU_T);
|
||||
if (g_f8_enabled) { /* F8 показываем только когда переключение кодировки возможно */
|
||||
put_str_attr(7 * 8 + 1, MENU_ROW, "F8", ATTR_MENU_K);
|
||||
put_str_attr(7 * 8 + 4, MENU_ROW, "Codepage", ATTR_MENU_T);
|
||||
|
||||
char num[3]; num[2] = 0;
|
||||
for (uint8_t i = 0; i < 9; i++) { /* F1..F9: ' 1'..' 9' */
|
||||
num[0] = ' '; num[1] = (char)('1' + i);
|
||||
put_str_attr((uint8_t)(i * 8), MENU_ROW, num, ATTR_MENU_K);
|
||||
}
|
||||
put_str_attr(9 * 8 + 0, MENU_ROW, "F10", ATTR_MENU_K);
|
||||
put_str_attr(9 * 8 + 4, MENU_ROW, "Exit", ATTR_MENU_T);
|
||||
put_str_attr(9 * 8, MENU_ROW, "10", ATTR_MENU_K); /* F10 */
|
||||
|
||||
put_str_attr(0 * 8 + 2, MENU_ROW, "Help", ATTR_MENU_T);
|
||||
#if WITH_RAW
|
||||
if (g_ready) /* RAW доступен после постройки документа */
|
||||
put_str_attr(1 * 8 + 2, MENU_ROW, (g_view == VIEW_MD) ? "RAW" : "MD", ATTR_MENU_T);
|
||||
if (g_view != VIEW_MD) /* Wrap/Unwrap — только в RAW */
|
||||
put_str_attr(2 * 8 + 2, MENU_ROW,
|
||||
(g_view == VIEW_RAW_WRAP) ? "UnWrap" : "Wrap", ATTR_MENU_T);
|
||||
#endif
|
||||
if (g_f8_enabled) /* смена кодировки сейчас возможна */
|
||||
put_str_attr(7 * 8 + 2, MENU_ROW, "CodePg", ATTR_MENU_T);
|
||||
put_str_attr(9 * 8 + 2, MENU_ROW, "Exit", ATTR_MENU_T);
|
||||
}
|
||||
|
||||
/* ==================================================================
|
||||
@@ -2370,6 +2376,22 @@ static void load_key(uint16_t k)
|
||||
/* Кооперативный «тик» прогрессивной загрузки — вызывается из index_lines(). */
|
||||
static void progress_tick(void)
|
||||
{
|
||||
#if WITH_RAW
|
||||
/* В RAW-режиме буфер документа готов сразу после конвертации (до индекса),
|
||||
* поэтому первый экран RAW можно показать немедленно — рисуем raw_draw, а
|
||||
* не MD-вид (иначе мелькал бы MD). Индекс при этом строится дальше (нужен
|
||||
* для MD-вида). */
|
||||
if (g_view != VIEW_MD) {
|
||||
if (!g_first_drawn) {
|
||||
raw_draw();
|
||||
raw_status_numbers();
|
||||
g_first_drawn = 1;
|
||||
cur_page = 0xFF; /* raw_draw сбил W3-маппинг, на который опирается fb() */
|
||||
}
|
||||
spinner_tick();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
/* Прогрессивная отрисовка строящегося (всегда показываемого) набора. */
|
||||
uint8_t touched = 0;
|
||||
if (!g_first_drawn && drawable_lines() >= VIEW_H) {
|
||||
@@ -2403,20 +2425,30 @@ static void progress_tick(void)
|
||||
* живых глобалах, а progress_tick через свап показывает/листает уже готовый
|
||||
* g_disp_doc. Возврат: 0 — успех, <0 — нехватка EMM (-5/-6/-7).
|
||||
*
|
||||
* UTF-8 конвертация делается ЛЕНИВО здесь, а не до первого экрана: при входе
|
||||
* (для DOC_UTF8 ещё не сконвертированного) живой буфер обязан быть = оригинал
|
||||
* (8-битные байты) — utf8_convert читает его через fb(). Это так и в случае
|
||||
* первичного UTF-8 (сразу после load_file), и вторичного (после сборки
|
||||
* первичного 8-битного, чей буфер = оригинал). */
|
||||
* UTF-8 набор конвертируется ИНКРЕМЕНТАЛЬНО: выделяем страницы, переключаем
|
||||
* живой буфер на utf и запускаем конвертацию первого чанка; дальше index_lines
|
||||
* докручивает её впереди позиции чтения (g_utf_building). Конвертер читает
|
||||
* оригинал из orig_file_phys[] через cv_read (не через fb, который теперь
|
||||
* указывает на utf). */
|
||||
static int build_doc(uint8_t slot, uint8_t enc, uint8_t visible)
|
||||
{
|
||||
if (slot == DOC_UTF8) {
|
||||
if (!utf_avail) { /* конвертируем сейчас (live = оригинал) */
|
||||
if (!alloc_and_convert_utf8()) return -7;
|
||||
if (!utf_avail) { /* первый вход: выделить страницы */
|
||||
if (!utf_alloc()) return -7;
|
||||
utf_avail = 1;
|
||||
conv_n = 0; conv_page = 0; conv_off = 0;
|
||||
utf_src = 0;
|
||||
g_utf_building = 1;
|
||||
}
|
||||
file_blk = utf_blk; file_pages = utf_pages; file_size = utf_size;
|
||||
file_blk = utf_blk; file_pages = utf_pages;
|
||||
memcpy(file_phys, utf_phys, MAX_PAGES);
|
||||
if (g_utf_building) {
|
||||
file_size = 0;
|
||||
cur_page = 0xFF;
|
||||
utf_convert_more(CONV_MARGIN); /* первый чанк — для первого экрана */
|
||||
} else {
|
||||
file_size = utf_size;
|
||||
}
|
||||
} else {
|
||||
file_blk = orig_file_blk; file_pages = orig_file_pages; file_size = orig_file_size;
|
||||
memcpy(file_phys, orig_file_phys, MAX_PAGES);
|
||||
@@ -2436,6 +2468,10 @@ static int build_doc(uint8_t slot, uint8_t enc, uint8_t visible)
|
||||
index_lines();
|
||||
g_loading = 0;
|
||||
|
||||
/* Зафиксировать конвертацию (на случай обрыва индекса по max_lines до конца
|
||||
* исходника) — иначе g_utf_building остался бы 1 и сломал бы следующую сборку. */
|
||||
if (g_utf_building) { utf_size = file_size; g_utf_building = 0; }
|
||||
|
||||
g_doc[slot].built = 1;
|
||||
doc_save_live(slot);
|
||||
return 0;
|
||||
@@ -2445,6 +2481,10 @@ static int build_doc(uint8_t slot, uint8_t enc, uint8_t visible)
|
||||
* 8-бит↔8-бит внутри одного набора — мгновенный ремап; переход в другой набор
|
||||
* — мгновенный doc_switch, если он построен, иначе ЛЕНИВАЯ сборка по месту
|
||||
* (со спиннером), результат кэшируется. */
|
||||
/* Меняет кодировку: тот же набор — ремап; другой готовый — doc_switch;
|
||||
* непостроенный — ЛЕНИВАЯ ТИХАЯ сборка (спиннер, без отрисовки документа).
|
||||
* НЕ перерисовывает документ/статус — это делает вызывающий в своём режиме
|
||||
* (MD: draw_viewport_from_cache; RAW: raw_draw), чтобы не мелькал чужой вид. */
|
||||
static void switch_encoding(uint8_t next)
|
||||
{
|
||||
uint8_t target = (next == ENC_UTF8) ? DOC_UTF8 : DOC_8BIT;
|
||||
@@ -2456,20 +2496,24 @@ static void switch_encoding(uint8_t next)
|
||||
set_encoding(next); g_doc[target].enc = next;
|
||||
} else { /* строим набор лениво */
|
||||
doc_save_live(g_active_doc); /* сохранить текущий (позиция и т.п.) */
|
||||
#if WITH_RAW
|
||||
g_ready = 0; render_menu(); /* идёт индексация — прячем F2 */
|
||||
#endif
|
||||
spinner_show(1);
|
||||
int rc = build_doc(target, next, 1); /* visible: рисует прогрессивно */
|
||||
int rc = build_doc(target, next, 1); /* visible: прогрессивный показ первого экрана
|
||||
* (progress_tick рисует по текущему виду — MD/RAW) */
|
||||
spinner_show(0);
|
||||
#if WITH_RAW
|
||||
g_ready = 1; render_menu();
|
||||
#endif
|
||||
if (rc < 0) { /* нет EMM — откат на текущий набор */
|
||||
if (target == DOC_UTF8) g_utf_failed = 1;
|
||||
doc_load_live(g_active_doc);
|
||||
clamp_top(); draw_viewport_from_cache(); render_full_status();
|
||||
return;
|
||||
}
|
||||
g_disp_doc = target; /* build_doc уже выставил active/enc */
|
||||
}
|
||||
clamp_top();
|
||||
draw_viewport_from_cache();
|
||||
render_full_status();
|
||||
clamp_top(); /* привести top_line к новому набору (для MD) */
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
@@ -2581,6 +2625,9 @@ int main(int argc, char **argv)
|
||||
* счётчиком строк. */
|
||||
spinner_show(0);
|
||||
g_f8_enabled = 1;
|
||||
#if WITH_RAW
|
||||
g_ready = 1; /* документ построен — теперь доступен F2 (RAW) */
|
||||
#endif
|
||||
render_menu();
|
||||
clamp_top();
|
||||
render_full_status();
|
||||
@@ -2595,6 +2642,65 @@ int main(int argc, char **argv)
|
||||
if (ascii == 0x1B) break; /* клавиша Esc */
|
||||
continue;
|
||||
}
|
||||
|
||||
#if WITH_RAW
|
||||
/* F2 — тумблер RAW ↔ MD (рабочий только после постройки документа). */
|
||||
if (scan == KEY_F2) {
|
||||
if (!g_ready) continue;
|
||||
if (g_view == VIEW_MD) {
|
||||
g_view = g_raw_last; /* → RAW (последний под-режим) */
|
||||
raw_seed_from(top_line); /* позиция из MD */
|
||||
raw_screen_init();
|
||||
render_full_status(); render_menu();
|
||||
raw_status_numbers(); raw_draw();
|
||||
} else {
|
||||
g_raw_last = g_view; /* запомнить под-режим RAW */
|
||||
g_view = VIEW_MD;
|
||||
clamp_top();
|
||||
render_full_status(); render_menu();
|
||||
draw_viewport_from_cache();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* RAW-режим: F3 Wrap/Unwrap, своя навигация; F8 — кодировка; F1/F10. */
|
||||
if (g_view != VIEW_MD) {
|
||||
if (scan == KEY_F10) goto exit_loop;
|
||||
if (scan == KEY_F1) {
|
||||
show_help();
|
||||
raw_screen_init();
|
||||
render_full_status(); render_menu();
|
||||
raw_status_numbers(); raw_draw();
|
||||
continue;
|
||||
}
|
||||
if (scan == KEY_F3) { /* Wrap ↔ Unwrap */
|
||||
g_view = (g_view == VIEW_RAW_WRAP) ? VIEW_RAW_HSCROLL : VIEW_RAW_WRAP;
|
||||
g_raw_last = g_view;
|
||||
raw_renorm();
|
||||
raw_screen_init();
|
||||
render_menu();
|
||||
raw_status_numbers(); raw_draw();
|
||||
continue;
|
||||
}
|
||||
if (scan == KEY_F8) {
|
||||
uint8_t next = (g_encoding == ENC_CP866) ? ENC_CP1251 :
|
||||
(g_encoding == ENC_CP1251) ? ENC_KOI8R :
|
||||
(g_encoding == ENC_KOI8R) ? ENC_UTF8 : ENC_CP866;
|
||||
if (next == ENC_UTF8 && g_utf_failed) next = ENC_CP866;
|
||||
uint8_t target = (next == ENC_UTF8) ? DOC_UTF8 : DOC_8BIT;
|
||||
/* другой буфер (в/из UTF-8) → в начало ДО сборки, чтобы прогрессивный
|
||||
* raw_draw в progress_tick показывал верх нового набора. */
|
||||
if (target != g_active_doc) raw_home();
|
||||
switch_encoding(next); /* меняет набор/ремап */
|
||||
if (g_abort) goto exit_loop;
|
||||
render_full_status(); raw_status_numbers(); raw_draw();
|
||||
continue;
|
||||
}
|
||||
raw_key(scan);
|
||||
continue;
|
||||
}
|
||||
#endif /* WITH_RAW */
|
||||
|
||||
switch (scan) {
|
||||
case KEY_F1: show_help(); break;
|
||||
case KEY_F8: { /* цикл CP866→CP1251→KOI8R→UTF8→CP866; нужный набор дособерётся лениво */
|
||||
@@ -2604,6 +2710,8 @@ int main(int argc, char **argv)
|
||||
if (next == ENC_UTF8 && g_utf_failed) next = ENC_CP866; /* UTF-8 недоступен */
|
||||
switch_encoding(next);
|
||||
if (g_abort) goto exit_loop; /* Esc/F10 во время ленивой досборки */
|
||||
render_full_status(); /* перерисовка — в MD-режиме */
|
||||
draw_viewport_from_cache();
|
||||
continue;
|
||||
}
|
||||
case KEY_F10: goto exit_loop;
|
||||
|
||||
Reference in New Issue
Block a user