mdview2: полировка статус-бара/справки/детекции + libc min/max

- status-bar: dirty-tracking (числа/процент/кодировка перерисовываются
  только при изменении), поле кодировки сдвинуто к DIV1_X-10 (8 симв.)
- md_key: HOME/END не перерисовывают экран, если позиция не меняется
- help: версия v1.0(a3), добавлены F2/F3 (RAW/Wrap), компактные секции
- enc: детекция по 5 частотным буквам и сэмплу 1КБ; ENC_UNSUPPORTED (UTF16/32)
- libc: добавлены min()/max() (naked, <stdlib.h>) + сборка в lib/Makefile

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-30 20:43:04 +03:00
parent 1b78dda125
commit 733746572c
9 changed files with 260 additions and 104 deletions
+107 -50
View File
@@ -28,6 +28,7 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <bios/text.h>
@@ -269,6 +270,7 @@ static void doc_switch(uint8_t target); /* forward (нужна в progress_tic
void put_str_attr(uint8_t x, uint8_t y, const char *s, uint8_t attr)
{
uint8_t len = (x < SCREEN_W) ? (uint8_t)(SCREEN_W - x) : 0;
len = min(len, strlen(s));
if (len == 0) return;
bios_set_place(y, x);
bios_writeattr_until(s, len, attr, 0);
@@ -490,7 +492,7 @@ static int alloc_set_storage(void)
{
uint8_t want;
want = (uint8_t)(file_pages >> 1 + 1u);
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;
@@ -499,7 +501,7 @@ static int alloc_set_storage(void)
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);
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;
@@ -616,32 +618,67 @@ static void load_key(uint16_t k)
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 игнорируем */
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_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-битный
* (ремап на отрисовке; индексация не зависит от кодировки) */
if (g_f8_enabled && g_active_doc == DOC_8BIT) {
uint8_t nx = (g_encoding == ENC_CP866) ? ENC_CP1251 :
(g_encoding == ENC_CP1251) ? ENC_KOI8R : ENC_CP866;
set_encoding(nx); g_doc[DOC_8BIT].enc = nx;
draw_viewport_from_cache();
render_full_status();
}
return;
default: return;
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_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-битный
* (ремап на отрисовке; индексация не зависит от кодировки) */
if (g_f8_enabled && g_active_doc == DOC_8BIT) {
uint8_t nx = (g_encoding == ENC_CP866) ? ENC_CP1251 :
(g_encoding == ENC_CP1251) ? ENC_KOI8R : ENC_CP866;
set_encoding(nx);
g_doc[DOC_8BIT].enc = nx;
draw_viewport_from_cache();
render_full_status();
}
return;
default:
return;
}
if (nt > maxtop) nt = maxtop;
if (nt > maxtop)
nt = maxtop;
if (nt != top_line) {
top_line = nt;
draw_viewport_from_cache();
@@ -676,10 +713,11 @@ void progress_tick(void)
g_first_drawn = 1;
touched = 1;
}
if (n_lines >= (uint16_t)(g_last_prog + 20)) {
if (n_lines >= (uint16_t)(g_last_prog + 10)) {
g_last_prog = n_lines;
spinner_tick();
if (g_first_drawn) render_md_status_numbers();
if (g_first_drawn)
render_md_status_numbers();
touched = 1;
}
if (g_first_drawn && kbhit()) {
@@ -688,7 +726,8 @@ void progress_tick(void)
}
/* Отрисовка/WINREST/BIOS могли сбить маппинг W3, на который опирается fb()
* при индексации → форсируем ре-маппинг страницы файла на следующем fb(). */
if (touched) cur_page = 0xFF;
if (touched)
cur_page = 0xFF;
}
/* ==================================================================
@@ -710,10 +749,12 @@ static int build_doc(uint8_t slot, uint8_t enc, uint8_t visible)
{
if (slot == DOC_UTF8) {
if (!utf_avail) { /* первый вход: выделить страницы */
if (!utf_alloc()) return -7; /* выделяет страницы + сбрасывает конвертер */
if (!utf_alloc())
return -7; /* выделяет страницы + сбрасывает конвертер */
utf_avail = 1;
}
file_blk = utf_blk; file_pages = utf_pages;
file_blk = utf_blk;
file_pages = utf_pages;
memcpy(file_phys, utf_phys, MAX_PAGES);
if (g_utf_building) {
file_size = 0;
@@ -723,18 +764,25 @@ static int build_doc(uint8_t slot, uint8_t enc, uint8_t visible)
file_size = utf_size;
}
} else {
file_blk = orig_file_blk; file_pages = orig_file_pages; file_size = orig_file_size;
file_blk = orig_file_blk;
file_pages = orig_file_pages;
file_size = orig_file_size;
memcpy(file_phys, orig_file_phys, MAX_PAGES);
}
cur_page = 0xFF;
int rc = alloc_set_storage();
if (rc < 0) return rc;
if (rc < 0)
return rc;
set_encoding(enc);
top_line = 0; viewport_x = 0;
top_line = 0;
viewport_x = 0;
g_active_doc = slot;
if (visible) { g_disp_doc = slot; g_first_drawn = 0; }
if (visible) {
g_disp_doc = slot;
g_first_drawn = 0;
}
g_loading = 1;
g_last_prog = 0;
@@ -743,7 +791,10 @@ static int build_doc(uint8_t slot, uint8_t enc, uint8_t visible)
/* Зафиксировать конвертацию (на случай обрыва индекса по max_lines до конца
* исходника) — иначе g_utf_building остался бы 1 и сломал бы следующую сборку. */
if (g_utf_building) { utf_size = file_size; g_utf_building = 0; }
if (g_utf_building) {
utf_size = file_size;
g_utf_building = 0;
}
g_doc[slot].built = 1;
doc_save_live(slot);
@@ -762,31 +813,37 @@ static void switch_encoding(uint8_t next)
{
uint8_t target = (next == ENC_UTF8) ? DOC_UTF8 : DOC_8BIT;
if (target == g_active_doc) { /* тот же набор — только ремап */
set_encoding(next); g_doc[target].enc = next;
} else if (g_doc[target].built) { /* набор готов — мгновенный свап */
doc_switch(target); g_disp_doc = target;
set_encoding(next); g_doc[target].enc = next;
} else { /* строим набор лениво */
doc_save_live(g_active_doc); /* сохранить текущий (позиция и т.п.) */
if (target == g_active_doc) { /* тот же набор — только ремап */
set_encoding(next);
g_doc[target].enc = next;
} else if (g_doc[target].built) { /* набор готов — мгновенный свап */
doc_switch(target);
g_disp_doc = target;
set_encoding(next);
g_doc[target].enc = next;
} else { /* строим набор лениво */
doc_save_live(g_active_doc); /* сохранить текущий (позиция и т.п.) */
#if WITH_RAW
g_ready = 0; render_menu(); /* идёт индексация — прячем F2 */
g_ready = 0;
render_menu(); /* идёт индексация — прячем F2 */
#endif
spinner_show(1);
int rc = build_doc(target, next, 1); /* visible: прогрессивный показ первого экрана
* (progress_tick рисует по текущему виду — MD/RAW) */
int rc = build_doc(target, next, 1); /* visible: прогрессивный показ первого экрана
* (progress_tick рисует по текущему виду — MD/RAW) */
spinner_show(0);
#if WITH_RAW
g_ready = 1; render_menu();
g_ready = 1;
render_menu();
#endif
if (rc < 0) { /* нет EMM — откат на текущий набор */
if (target == DOC_UTF8) g_utf_failed = 1;
if (rc < 0) { /* нет EMM — откат на текущий набор */
if (target == DOC_UTF8)
g_utf_failed = 1;
doc_load_live(g_active_doc);
return;
}
g_disp_doc = target; /* build_doc уже выставил active/enc */
g_disp_doc = target; /* build_doc уже выставил active/enc */
}
clamp_top(); /* привести top_line к новому набору (для MD) */
clamp_top(); /* привести top_line к новому набору (для MD) */
}
/* ==================================================================