mdview2: статус-бар в отдельный модуль, упростить alloc_set_storage
- mdview2_status.c: вынести статус-бар/меню/спиннер из ядра - mdview2.c: убрать retry-цикл в alloc_set_storage (fail-fast вместо ложной устойчивости — при нехватке EMM под индекс контент тоже не влезет) - mdview2.h: дополнить экспортами статус-модуля - mdview2_md.c / mdview2_raw.c: зачистка после расщепления - mdview/mdview.c: переименовать scroll_* → md_scroll_* (симметрия) - docs/fast_ram.md, docs/turboc.txt: добавить справочные доки - examples/mdview2/README.MD, READMEBG.MD: обновить описание Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+155
-190
@@ -35,6 +35,7 @@
|
||||
#include <unistd.h>
|
||||
#include <sprinter.h>
|
||||
#include <sprinter_mem.h>
|
||||
#include <sprinter_exit.h>
|
||||
#include <palette.h>
|
||||
#include "mdview2.h" /* геометрия, атрибуты, общий интерфейс модулей */
|
||||
|
||||
@@ -94,7 +95,7 @@ static const uint8_t md_pallete[16 * 4] = {
|
||||
/* cache_rec_t (РОВНО 8 байт, см. sprinter_winrest_format) — в mdview2.h. */
|
||||
|
||||
#define CACHE_DIR_REC_SIZE ((uint16_t)sizeof(cache_rec_t))
|
||||
#define CACHE_DIR_RECS_PER_PAGE 2048u
|
||||
#define CACHE_DIR_RECS_PER_PAGE (16384 / CACHE_DIR_REC_SIZE) /* 2048 */
|
||||
#define MAX_CACHE_DIR_PAGES 8u
|
||||
/* MAX_CACHE_LINE_LEN — в mdview2.h (буфер ячеек g_cells живёт в mdview2_index.c). */
|
||||
#define MAX_CACHE_CONTENT_PAGES 64u /* запас: до 1 МБ кэша контента */
|
||||
@@ -113,14 +114,14 @@ static uint16_t cache_cur_off = 0; /* текущее смещ
|
||||
void cache_dir_get(uint16_t idx, cache_rec_t *r)
|
||||
{
|
||||
uint8_t page = (uint8_t)(idx >> 11);
|
||||
uint16_t off = (uint16_t)((idx & 2047u) << 3);
|
||||
uint16_t off = (uint16_t)((idx & 0x07FF) << 3); /* 2047 */
|
||||
bank_read(cache_dir_phys[page], off, r, CACHE_DIR_REC_SIZE);
|
||||
}
|
||||
|
||||
void cache_dir_put(uint16_t idx, const cache_rec_t *r)
|
||||
{
|
||||
uint8_t page = (uint8_t)(idx >> 11);
|
||||
uint16_t off = (uint16_t)((idx & 2047u) << 3);
|
||||
uint16_t off = (uint16_t)((idx & 0x07FF) << 3); /* 2047 */
|
||||
bank_write(cache_dir_phys[page], off, r, CACHE_DIR_REC_SIZE);
|
||||
}
|
||||
|
||||
@@ -140,9 +141,11 @@ uint8_t cache_reserve(uint16_t nbytes, uint8_t *out_page, uint16_t *out_off)
|
||||
* не вмещает запись целиком. */
|
||||
uint8_t next_idx = (cache_content_n_pages == 0) ? 0 : (uint8_t)(cache_cur_page_idx + 1);
|
||||
if (next_idx >= cache_content_n_pages) {
|
||||
if (cache_content_n_pages >= MAX_CACHE_CONTENT_PAGES) return 0;
|
||||
if (cache_content_n_pages >= MAX_CACHE_CONTENT_PAGES)
|
||||
return 0;
|
||||
uint8_t blk = mem_alloc_pages(1);
|
||||
if (blk == 0) return 0;
|
||||
if (blk == 0)
|
||||
return 0;
|
||||
cache_content_blk[cache_content_n_pages] = blk;
|
||||
cache_content_phys[cache_content_n_pages] = mem_get_page(blk, 0);
|
||||
cache_content_n_pages++;
|
||||
@@ -171,40 +174,40 @@ void cache_commit(uint8_t page, uint16_t off, const void *buf, uint16_t len)
|
||||
/* emph_to_attr определена ниже (рядом с inline-логикой), нужна в inline_scan. */
|
||||
/* emph_to_attr — в mdview2_index.c. */
|
||||
|
||||
uint16_t n_lines = 0; /* extern: вью читает */
|
||||
uint16_t max_lines = 0; /* ёмкость индекса: index_pages * 2048 */
|
||||
uint16_t top_line = 0; /* extern: вью/скролл */
|
||||
uint32_t file_size = 0;
|
||||
uint16_t n_lines = 0; /* extern: всего строк в индексе текущего набора */
|
||||
uint16_t max_lines = 0; /* extern: ёмкость индекса: index_pages * 2048 */
|
||||
uint16_t top_line = 0; /* extern: верхняя видимая строка */
|
||||
uint32_t file_size = 0; /* extern: размер активного буфера документа */
|
||||
static uint8_t file_blk = 0;
|
||||
static uint8_t file_pages = 0;
|
||||
static uint8_t file_phys[MAX_PAGES] = {0};
|
||||
uint8_t index_blk = 0;
|
||||
uint8_t index_pages = 0;
|
||||
uint8_t index_phys[MAX_INDEX_PAGES] = {0};
|
||||
uint8_t index_truncated = 0; /* выставляется при исчерпании ёмкости индекса */
|
||||
uint8_t index_blk = 0; /* extern: дескриптор EMM-блока индекса */
|
||||
uint8_t index_pages = 0; /* */
|
||||
uint8_t index_phys[MAX_INDEX_PAGES] = {0}; /* extern: физ. страницы индекса */
|
||||
uint8_t index_truncated = 0; /* выставляется при исчерпании ёмкости индекса */
|
||||
|
||||
/* cur_seg_off/cur_rec (near-состояние индексатора) — в mdview2_index.c. */
|
||||
uint8_t cur_page = 0xFF; /* 0xFF = в W3 ещё не показана ни одна страница (extern: кодек инвалидирует) */
|
||||
uint8_t viewport_x = 0; /* горизонтальный сдвиг для nowrap-строк (extern: вью) */
|
||||
char filename[64] = {0}; /* extern: статус-бар */
|
||||
uint8_t cur_page = 0xFF; /* extern: 0xFF = в W3 ещё не показана ни одна страница (extern: кодек инвалидирует) */
|
||||
uint8_t viewport_x = 0; /* extern: горизонтальный сдвиг для nowrap-строк */
|
||||
char filename[64] = {0}; /* extern: статус-бар */
|
||||
|
||||
/* Прогрессивная загрузка: рисуем первый экран и пускаем скролл, пока
|
||||
* index_lines() ещё досканирует файл (кооперативно, без прерываний). */
|
||||
uint8_t g_view = VIEW_MD; /* extern: активный режим просмотра (существует даже когда доступен только MD режим) */
|
||||
#if WITH_RAW
|
||||
uint8_t g_view = VIEW_MD; /* режим просмотра (общий с mdview2_raw.c) */
|
||||
uint8_t g_ready = 0; /* 1 когда первичный документ построен — F2 (RAW) рабочий (extern: вью) */
|
||||
static uint8_t g_raw_last = VIEW_RAW_WRAP; /* последний под-режим RAW (возврат по F2) */
|
||||
uint8_t g_ready = 0; /* extern: 1 когда первичный документ построен — F2 (RAW) рабочий */
|
||||
static uint8_t g_raw_last = VIEW_RAW_WRAP; /* последний под-режим RAW (возврат по F2) */
|
||||
#endif
|
||||
uint8_t g_loading = 0; /* 1 во время index_lines() (extern: вью) */
|
||||
static uint8_t g_first_drawn = 0; /* первый экран уже нарисован */
|
||||
uint8_t g_f8_enabled = 0; /* можно ли сейчас переключать кодировку (extern: вью-меню) */
|
||||
static uint8_t g_utf_failed = 0; /* UTF-8 набор не удалось построить (нет EMM) — пропускаем UTF-8 в цикле F8 */
|
||||
uint8_t g_abort = 0; /* F10/Esc во время загрузки → прервать индексацию */
|
||||
static uint16_t g_last_prog = 0; /* n_lines на последнем обновлении статуса */
|
||||
/* progress_tick — объявлена в mdview2.h (определена ниже, зовётся из index_lines). */
|
||||
uint8_t g_loading = 0; /* extern: 1 во время index_lines() */
|
||||
static uint8_t g_first_drawn = 0; /* первый экран уже нарисован */
|
||||
uint8_t g_f8_enabled = 0; /* extern: можно ли сейчас переключать кодировку */
|
||||
static uint8_t g_utf_failed = 0; /* UTF-8 набор не удалось построить (нет EMM) — пропускаем UTF-8 в цикле F8 */
|
||||
uint8_t g_abort = 0; /* extern: F10/Esc во время загрузки → прервать индексацию */
|
||||
static uint16_t g_last_prog = 0; /* n_lines на последнем обновлении статуса */
|
||||
|
||||
/* Кодировки (ENC_*) и g_encoding — в mdview2.h / mdview2_enc.c. */
|
||||
const uint8_t *g_remap = 0; /* активная таблица ремапа (CP1251/KOI8), 0 = без ремапа */
|
||||
uint8_t g_scratch_phys = 0; /* EMM-страница под ремап-буфер строки для win_rest */
|
||||
const uint8_t *g_remap = 0; /* extern: активная таблица ремапа (CP1251/KOI8), 0 = без ремапа (CP866)*/
|
||||
uint8_t g_scratch_phys = 0; /* extern: EMM-страница под ремап-буфер строки для win_rest */
|
||||
|
||||
/* UTF-8 набор (utf_blk/utf_pages/utf_phys/utf_size/utf_avail, конвертер и его
|
||||
* состояние) — в mdview2_enc.c; разделяемые символы и API — в mdview2.h.
|
||||
@@ -214,11 +217,11 @@ uint8_t g_scratch_phys = 0; /* EMM-страница под ремап
|
||||
/* Снимок исходного (8-битного) файлового буфера — набор DOC_8BIT читает его
|
||||
* как есть, набор DOC_UTF8 читает utf_phys. */
|
||||
static uint8_t orig_file_blk = 0;
|
||||
uint8_t orig_file_pages = 0; /* extern: читает кодек (utf_alloc) */
|
||||
uint32_t orig_file_size = 0; /* extern: читает кодек (utf_convert_more) */
|
||||
uint8_t orig_file_phys[MAX_PAGES] = {0}; /* extern: читает кодек (cv_read) */
|
||||
uint8_t orig_file_pages = 0; /* extern: читает кодек (utf_alloc) */
|
||||
uint32_t orig_file_size = 0; /* extern: читает кодек (utf_convert_more) */
|
||||
uint8_t orig_file_phys[MAX_PAGES] = {0}; /* extern: читает кодек (cv_read) */
|
||||
|
||||
/* ---------- Два сосуществующих набора индекс/кэш (Фазы 4-5) ----------
|
||||
/* ----------------- Два сосуществующих набора индекс/кэш -----------------
|
||||
* DOC_8BIT — исходные байты (CP866/CP1251/KOI8R, различаются ремапом на
|
||||
* отрисовке); DOC_UTF8 — UTF-8, декодированный в CP866. Оба строятся eager
|
||||
* при старте (кооперативно), поэтому F8 переключает активный набор мгновенно.
|
||||
@@ -242,6 +245,7 @@ typedef struct {
|
||||
uint16_t top_line;
|
||||
uint8_t viewport_x;
|
||||
} docset_t;
|
||||
|
||||
#define DOC_8BIT 0
|
||||
#define DOC_UTF8 1
|
||||
static docset_t g_doc[2] = {{0}, {0}}; /* ОБЯЗАТЕЛЬНО инициализировать: built=0 для обоих
|
||||
@@ -252,13 +256,7 @@ static uint8_t g_disp_doc = DOC_8BIT; /* какой набор показа
|
||||
|
||||
static void doc_switch(uint8_t target); /* forward (нужна в progress_tick) */
|
||||
|
||||
/* Спиннер в статус-баре (col=8,row=0).
|
||||
* Включается во время загрузки/индексации, чтобы показать,
|
||||
* что программа занята и не зависла. */
|
||||
#define SPINNER_COL 8
|
||||
static const char spinner_chars[4] = { '|', '/', '-', '\\' };
|
||||
static uint8_t spinner_phase = 0;
|
||||
static uint8_t spinner_active = 0;
|
||||
/* Статус-бар, меню и спиннер — в mdview2_status.c. */
|
||||
|
||||
/* ==================================================================
|
||||
* Примитивы экрана, доступ к EMM/окну W3 (fb/win_rest), палитра
|
||||
@@ -270,50 +268,32 @@ static uint8_t spinner_active = 0;
|
||||
* можно передать в BIOS LP_PRINT_LN напрямую, без промежуточной копии. */
|
||||
void put_str_attr(uint8_t x, uint8_t y, const char *s, uint8_t attr)
|
||||
{
|
||||
uint8_t maxlen = (x < SCREEN_W) ? (uint8_t)(SCREEN_W - x) : 0;
|
||||
uint8_t len = (uint8_t)strlen(s);
|
||||
if (len > maxlen) len = maxlen;
|
||||
uint8_t len = (x < SCREEN_W) ? (uint8_t)(SCREEN_W - x) : 0;
|
||||
if (len == 0) return;
|
||||
bios_set_place(y, x);
|
||||
bios_writeattr(s, len, attr);
|
||||
bios_writeattr_until(s, len, attr, 0);
|
||||
}
|
||||
|
||||
|
||||
/* Полностью очищает указанную строку экрана символами пробела с attr. */
|
||||
void fill_row(uint8_t y, uint8_t attr)
|
||||
{
|
||||
// bios_set_place(y, 0);
|
||||
// bios_fillcharattr(' ', attr, SCREEN_W);
|
||||
bios_clearwin(y, 0, 1, SCREEN_W, attr);
|
||||
}
|
||||
|
||||
/* Продвигает спиннер на один кадр.
|
||||
* Если спиннер выключен — функция ничего не делает. */
|
||||
void spinner_tick(void)
|
||||
{
|
||||
if (!spinner_active) return;
|
||||
wrchar(SPINNER_COL, 0, spinner_chars[spinner_phase & 3], ATTR_BAR_SPINNER);
|
||||
spinner_phase++;
|
||||
}
|
||||
|
||||
/* Включает/выключает спиннер.
|
||||
* При выключении очищает его позицию пробелом в статус-баре. */
|
||||
static void spinner_show(uint8_t on)
|
||||
{
|
||||
spinner_active = on;
|
||||
if (!on) wrchar(SPINNER_COL, 0, ' ', ATTR_BAR);
|
||||
}
|
||||
|
||||
/* Отображает указанную EMM-страницу файла в окно W3.
|
||||
* Если эта страница уже активна, переключение не выполняется. */
|
||||
static inline void map_page(uint8_t page)
|
||||
{
|
||||
if (page != cur_page) {
|
||||
/* file_phys[] позволяет не вызывать mem_get_page() на каждом свопе. */
|
||||
sprinter_page_w3(file_phys[page]);
|
||||
cur_page = page;
|
||||
}
|
||||
}
|
||||
// static inline void map_page(uint8_t page)
|
||||
// {
|
||||
// if (page != cur_page) {
|
||||
// /* file_phys[] позволяет не вызывать mem_get_page() на каждом свопе. */
|
||||
// sprinter_page_w3(file_phys[page]);
|
||||
// cur_page = page;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
/* Читает один байт логического файла по смещению p.
|
||||
* При переходе между страницами автоматически переключает W3.
|
||||
* Проверка границ выполняется вызывающей стороной.
|
||||
@@ -458,13 +438,15 @@ static void set_pallete(void) {
|
||||
static int load_file(const char *path)
|
||||
{
|
||||
int fd = open(path, O_RDONLY);
|
||||
if (fd < 0) return -1;
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
long sz = lseek(fd, 0, SEEK_END);
|
||||
if (sz < 0 || (uint32_t)sz > MAX_FILE) {
|
||||
close(fd);
|
||||
return -2;
|
||||
}
|
||||
|
||||
file_size = (uint32_t)sz;
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
|
||||
@@ -506,40 +488,25 @@ static int load_file(const char *path)
|
||||
* Возврат: 0 — успех, -5/-6 — нехватка EMM. */
|
||||
static int alloc_set_storage(void)
|
||||
{
|
||||
/* Блок индекса (8 байт на сегмент, 2048 записей/страница). */
|
||||
{
|
||||
uint8_t want = (uint8_t)(file_pages + 1u);
|
||||
if (want > MAX_INDEX_PAGES) want = MAX_INDEX_PAGES;
|
||||
index_blk = 0;
|
||||
while (want >= 1u) {
|
||||
index_blk = mem_alloc_pages(want);
|
||||
if (index_blk) break;
|
||||
want--;
|
||||
}
|
||||
if (index_blk == 0) return -5;
|
||||
index_pages = want;
|
||||
for (uint8_t i = 0; i < index_pages; i++)
|
||||
index_phys[i] = mem_get_page(index_blk, i);
|
||||
max_lines = (uint16_t)index_pages * INDEX_RECS_PER_PAGE;
|
||||
}
|
||||
uint8_t want;
|
||||
|
||||
/* Директория рендер-кэша — та же ёмкость, что у индекса. */
|
||||
{
|
||||
uint8_t want = (uint8_t)(file_pages + 1u);
|
||||
if (want > MAX_CACHE_DIR_PAGES) want = MAX_CACHE_DIR_PAGES;
|
||||
cache_dir_blk = 0;
|
||||
while (want >= 1u) {
|
||||
cache_dir_blk = mem_alloc_pages(want);
|
||||
if (cache_dir_blk) break;
|
||||
want--;
|
||||
}
|
||||
if (cache_dir_blk == 0) return -6;
|
||||
cache_dir_pages = want;
|
||||
for (uint8_t i = 0; i < cache_dir_pages; i++)
|
||||
cache_dir_phys[i] = mem_get_page(cache_dir_blk, i);
|
||||
}
|
||||
want = (uint8_t)(file_pages >> 1 + 1u);
|
||||
if (want > MAX_INDEX_PAGES) want = MAX_INDEX_PAGES;
|
||||
index_blk = mem_alloc_pages(want);
|
||||
if (index_blk == 0) return -5;
|
||||
index_pages = want;
|
||||
for (uint8_t i = 0; i < index_pages; i++)
|
||||
index_phys[i] = mem_get_page(index_blk, i);
|
||||
max_lines = (uint16_t)index_pages * INDEX_RECS_PER_PAGE;
|
||||
|
||||
want = (uint8_t)(file_pages >> 1 + 1u);
|
||||
if (want > MAX_CACHE_DIR_PAGES) want = MAX_CACHE_DIR_PAGES;
|
||||
cache_dir_blk = mem_alloc_pages(want);
|
||||
if (cache_dir_blk == 0) return -6;
|
||||
cache_dir_pages = want;
|
||||
for (uint8_t i = 0; i < cache_dir_pages; i++)
|
||||
cache_dir_phys[i] = mem_get_page(cache_dir_blk, i);
|
||||
|
||||
/* Свежий пул контента под этот набор. */
|
||||
cache_content_n_pages = 0;
|
||||
cache_cur_page_idx = 0;
|
||||
cache_cur_off = 0;
|
||||
@@ -617,7 +584,8 @@ static void doc_load_live(uint8_t i)
|
||||
|
||||
static void doc_switch(uint8_t target)
|
||||
{
|
||||
if (target == g_active_doc) return;
|
||||
if (target == g_active_doc)
|
||||
return;
|
||||
doc_save_live(g_active_doc);
|
||||
doc_load_live(target);
|
||||
g_active_doc = target;
|
||||
@@ -628,18 +596,6 @@ static void doc_switch(uint8_t target)
|
||||
/* Отрисовка области/статус-бара/меню и прокрутка — в модуле mdview2_md.c. */
|
||||
|
||||
|
||||
/* ==================================================================
|
||||
* Вывод ошибки
|
||||
* ================================================================== */
|
||||
|
||||
/* Печатает сообщение об ошибке и ждёт нажатия клавиши. */
|
||||
static void die(const char *msg)
|
||||
{
|
||||
clrscr_attr(ATTR_TEXT);
|
||||
puts(msg);
|
||||
puts("Press any key to exit...");
|
||||
(void)getkey();
|
||||
}
|
||||
|
||||
/* ==================================================================
|
||||
* Прогрессивная загрузка и диспетчер клавиш (MD)
|
||||
@@ -668,10 +624,10 @@ static void load_key(uint16_t k)
|
||||
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_HOME: top_line = 0; viewport_x = 0; draw_viewport_from_cache(); render_md_status_numbers(); return;
|
||||
case KEY_END: top_line = maxtop; viewport_x = 0; draw_viewport_from_cache(); render_md_status_numbers(); return;
|
||||
case KEY_LEFT: md_scroll_horizon(-(int8_t)HPAN_STEP); render_md_status_numbers(); return;
|
||||
case KEY_RIGHT: md_scroll_horizon(+(int8_t)HPAN_STEP); render_md_status_numbers(); return;
|
||||
case KEY_F1: show_help(); return;
|
||||
case KEY_F8: /* во время сборки крутим ТОЛЬКО 8-бит и только если набор 8-битный
|
||||
* (ремап на отрисовке; индексация не зависит от кодировки) */
|
||||
@@ -689,7 +645,7 @@ static void load_key(uint16_t k)
|
||||
if (nt != top_line) {
|
||||
top_line = nt;
|
||||
draw_viewport_from_cache();
|
||||
render_status_numbers();
|
||||
render_md_status_numbers();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -704,7 +660,7 @@ void progress_tick(void)
|
||||
if (g_view != VIEW_MD) {
|
||||
if (!g_first_drawn) {
|
||||
raw_draw();
|
||||
raw_status_numbers();
|
||||
// render_raw_status();
|
||||
g_first_drawn = 1;
|
||||
cur_page = 0xFF; /* raw_draw сбил W3-маппинг, на который опирается fb() */
|
||||
}
|
||||
@@ -716,14 +672,14 @@ void progress_tick(void)
|
||||
uint8_t touched = 0;
|
||||
if (!g_first_drawn && drawable_lines() >= VIEW_H) {
|
||||
draw_viewport_from_cache();
|
||||
render_status_numbers();
|
||||
render_md_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();
|
||||
if (g_first_drawn) render_md_status_numbers();
|
||||
touched = 1;
|
||||
}
|
||||
if (g_first_drawn && kbhit()) {
|
||||
@@ -833,6 +789,15 @@ static void switch_encoding(uint8_t next)
|
||||
clamp_top(); /* привести top_line к новому набору (для MD) */
|
||||
}
|
||||
|
||||
/* ==================================================================
|
||||
* Вывод ошибки
|
||||
* ================================================================== */
|
||||
|
||||
static void on_exit(void) {
|
||||
unload_file();
|
||||
pal_reset(PAL_CGA);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *path;
|
||||
@@ -844,16 +809,20 @@ int main(int argc, char **argv)
|
||||
{
|
||||
int chk = open(path, O_RDONLY);
|
||||
if (chk < 0) {
|
||||
cputs("MDView v0.2 -- Markdown Viewer for Sprinter\r\n");
|
||||
cputs("(c) 2026 Petrov A.G.\r\n\r\n");
|
||||
cputs("Usage: mdview2 <file.md>\r\n");
|
||||
cputs(" Displays a Markdown document (CP866, up to 128 KB).\r\n");
|
||||
return 1;
|
||||
// settextmode(TEXT_MODE_80x32);
|
||||
clrscr_attr(ATTR_TEXT);
|
||||
cputs("MDView v0.3 -- Markdown Viewer for Sprinter\r\n" \
|
||||
"(c) 2026 Petrov A.G.\r\n\r\n" \
|
||||
"Usage: mdview2 <file.md>\r\n" \
|
||||
" Displays a Markdown document (CP866/CP1251/KOI8R/UTF8, up to 128 KB).\r\n");
|
||||
exit(-1);
|
||||
}
|
||||
close(chk);
|
||||
}
|
||||
}
|
||||
|
||||
atexit(on_exit);
|
||||
|
||||
/* Копируем путь в static filename[] для отображения в статус-баре. */
|
||||
{
|
||||
uint8_t i = 0;
|
||||
@@ -869,8 +838,10 @@ int main(int argc, char **argv)
|
||||
settextmode(TEXT_MODE_80x32);
|
||||
clrscr_attr(ATTR_TEXT);
|
||||
set_pallete();
|
||||
prerender_status();
|
||||
render_menu();
|
||||
render_full_status();
|
||||
render_percent_progress(0);
|
||||
spinner_show(1);
|
||||
|
||||
int rc = load_file(path);
|
||||
@@ -890,7 +861,7 @@ int main(int argc, char **argv)
|
||||
case -6: cputs("render-cache dir alloc failed"); break;
|
||||
}
|
||||
cputs("\n\r\n\r");
|
||||
return 1;
|
||||
exit(-1);
|
||||
}
|
||||
/* Scratch-страница под ремап-буфер строки (win_rest_remap). */
|
||||
g_scratch_phys = mem_get_page(mem_alloc_pages(1), 0);
|
||||
@@ -928,13 +899,15 @@ int main(int argc, char **argv)
|
||||
clrscr_attr(ATTR_RESET);
|
||||
textattr(ATTR_RESET);
|
||||
cputs("MDView: index/cache alloc failed\n\r\n\r");
|
||||
return 1;
|
||||
exit(-1);
|
||||
}
|
||||
if (g_abort) goto exit_loop; /* F10/Esc во время загрузки — корректный выход */
|
||||
if (g_abort)
|
||||
exit(0); /* F10/Esc во время загрузки — корректный выход */
|
||||
if (n_lines == 0) {
|
||||
die("mdview: empty file");
|
||||
unload_file();
|
||||
return 1;
|
||||
clrscr_attr(ATTR_RESET);
|
||||
textattr(ATTR_RESET);
|
||||
cputs("mdview: empty file");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/* Первичный готов — гасим спиннер, включаем F8 (второй набор дособерётся
|
||||
@@ -956,11 +929,17 @@ int main(int argc, char **argv)
|
||||
uint8_t scan = (uint8_t)((k >> 8) & 0x7F);
|
||||
|
||||
if (ascii) {
|
||||
if (ascii == 0x1B) break; /* клавиша Esc */
|
||||
if (ascii == 0x1B)
|
||||
break; /* клавиша Esc */
|
||||
continue;
|
||||
}
|
||||
|
||||
#if WITH_RAW
|
||||
if (scan == KEY_F10) {
|
||||
break;
|
||||
}
|
||||
if (scan == KEY_F1) {
|
||||
show_help();
|
||||
continue;
|
||||
} /* show_help сам перерисует */
|
||||
/* F2 — тумблер RAW ↔ MD (рабочий только после постройки документа). */
|
||||
if (scan == KEY_F2) {
|
||||
if (!g_ready) continue;
|
||||
@@ -968,8 +947,10 @@ int main(int argc, char **argv)
|
||||
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();
|
||||
status_encoding();
|
||||
render_menu();
|
||||
render_raw_status();
|
||||
raw_draw();
|
||||
} else {
|
||||
g_raw_last = g_view; /* запомнить под-режим RAW */
|
||||
g_view = VIEW_MD;
|
||||
@@ -979,64 +960,48 @@ int main(int argc, char **argv)
|
||||
}
|
||||
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 */
|
||||
|
||||
/* MD-режим: F1/F8/F10 здесь, навигация — в md_key() (peer к raw_key). */
|
||||
if (scan == KEY_F10) goto exit_loop;
|
||||
if (scan == KEY_F1) { show_help(); continue; } /* show_help сам перерисует */
|
||||
if (scan == KEY_F8) { /* цикл CP866→CP1251→KOI8R→UTF8→CP866; набор дособерётся лениво */
|
||||
/* цикл CP866→CP1251→KOI8R→UTF8→CP866; набор дособерётся лениво */
|
||||
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; /* UTF-8 недоступен */
|
||||
(g_encoding == ENC_CP1251) ? ENC_KOI8R :
|
||||
(g_encoding == ENC_KOI8R) ? ENC_UTF8 : ENC_CP866;
|
||||
if (next == ENC_UTF8 && g_utf_failed)
|
||||
next = ENC_CP866; /* UTF-8 недоступен */
|
||||
switch_encoding(next);
|
||||
if (g_abort) goto exit_loop; /* Esc/F10 во время ленивой досборки */
|
||||
if (g_abort)
|
||||
exit(0); /* Esc/F10 во время ленивой досборки */
|
||||
render_full_status(); /* перерисовка — в MD-режиме */
|
||||
draw_viewport_from_cache();
|
||||
continue;
|
||||
}
|
||||
md_key(scan); /* навигация MD (сама рисует+статус); незнакомые игнорит */
|
||||
switch(g_view) {
|
||||
case VIEW_MD:
|
||||
/* MD-режим: F1/F8/F10 здесь, навигация — в md_key() (peer к raw_key). */
|
||||
md_key(scan); /* навигация MD (сама рисует+статус); незнакомые игнорит */
|
||||
continue;
|
||||
#if WITH_RAW
|
||||
case VIEW_RAW_WRAP:
|
||||
case VIEW_RAW_HSCROLL:
|
||||
/* RAW-режим: F3 Wrap/Unwrap, своя навигация; F8 — кодировка; F1/F10. */
|
||||
/* Wrap ↔ Unwrap */
|
||||
if (scan == KEY_F3) {
|
||||
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();
|
||||
render_raw_status();
|
||||
raw_draw();
|
||||
continue;
|
||||
}
|
||||
raw_key(scan);
|
||||
continue;
|
||||
#endif /* WITH_RAW */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
exit_loop:
|
||||
unload_file();
|
||||
pal_reset(PAL_CGA);
|
||||
clrscr_attr(ATTR_TEXT);
|
||||
exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user