1b78dda125
- 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>
300 lines
11 KiB
C
300 lines
11 KiB
C
/*
|
|
* mdview2_raw.c — RAW-просмотр исходного текста (без markdown-форматирования).
|
|
*
|
|
* Работает по АКТИВНОМУ буферу документа через fb() (8-бит = исходные байты,
|
|
* UTF-8 = декодированный в CP866 буфер), ремап ≥0x80 (CP1251/KOI8) — на
|
|
* отрисовке. Индекс/кэш markdown не используются: позиция — байт-offset,
|
|
* 1 байт = 1 ячейка, \t показываем пробелом. Два режима:
|
|
* VIEW_RAW_WRAP — длинные строки переносятся кратно 80 (рвём слова);
|
|
* VIEW_RAW_HSCROLL — строка в один ряд, горизонтальный скролл.
|
|
*
|
|
* Память: 0 доп. EMM; near-буфер строки + переиспользование g_scratch_phys.
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <conio.h>
|
|
#include <bios/text.h>
|
|
#include "mdview2.h"
|
|
|
|
#if WITH_RAW /* весь модуль — опционален (mdview_conf.h); при 0 пустой объектник */
|
|
|
|
static uint32_t raw_top = 0; /* байт-offset верхней строки экрана */
|
|
static uint16_t raw_hpan = 0; /* гориз. сдвиг (только HSCROLL) */
|
|
|
|
/* ---- примитивы навигации по физическим строкам / переносам -------- */
|
|
|
|
static uint32_t raw_line_start(uint32_t off)
|
|
{
|
|
while (off && fb(off - 1) != '\n') off--;
|
|
return off;
|
|
}
|
|
|
|
/* WRAP: начало следующего ряда от off (\n завершает строку; иначе +80). */
|
|
static uint32_t raw_next_wrap(uint32_t off)
|
|
{
|
|
uint32_t e = off, lim = off + SCREEN_W;
|
|
while (e < file_size) {
|
|
if (fb(e) == '\n') return e + 1;
|
|
e++;
|
|
if (e >= lim) { /* прошли 80 без \n */
|
|
if (e < file_size && fb(e) == '\n') return e + 1; /* ровно 80 + \n */
|
|
return e; /* длинная строка → продолжение */
|
|
}
|
|
}
|
|
return e; /* EOF */
|
|
}
|
|
|
|
static uint32_t raw_prev_wrap(uint32_t off)
|
|
{
|
|
if (off == 0) return 0;
|
|
uint32_t pls = raw_line_start(off - 1); /* off-1 — это \n пред. строки */
|
|
while (pls + SCREEN_W < (off - 1)) pls += SCREEN_W;
|
|
return pls;
|
|
}
|
|
|
|
/* HSCROLL: начало следующей/предыдущей физической строки. */
|
|
static uint32_t raw_next_line(uint32_t off)
|
|
{
|
|
while (off < file_size && fb(off) != '\n') off++;
|
|
return (off < file_size) ? off + 1 : off;
|
|
}
|
|
|
|
static uint32_t raw_prev_line(uint32_t off)
|
|
{
|
|
if (off == 0) return 0;
|
|
return raw_line_start(off - 1);
|
|
}
|
|
|
|
/* ---- отрисовка ряда (общий near-буфер → g_scratch_phys → win_rest) - */
|
|
|
|
static uint8_t raw_buf[SCREEN_W + 1]; /* только символы (атрибут единый, задаётся при выводе) */
|
|
|
|
/* В RAW атрибут единый (ATTR_TEXT): буфер — только 80 символов, без пар
|
|
* (char,attr). Кладём один байт-глиф (\t→пробел, ремап ≥0x80). */
|
|
static void raw_cell(uint8_t i, uint8_t ch)
|
|
{
|
|
if (ch >= 0x80 && g_remap) ch = g_remap[ch - 0x80];
|
|
raw_buf[i] = ch;
|
|
}
|
|
/* Вывод готового 80-символьного буфера в строку экрана одним BIOS-вызовом
|
|
* (char-буфер + единый attr — без WINREST/scratch-страницы). */
|
|
static void raw_flush(uint8_t row)
|
|
{
|
|
bios_set_place(row, 0);
|
|
// bios_writeattr((const char *)raw_buf, SCREEN_W, ATTR_TEXT);
|
|
// bios_writeattr_until((const char *)raw_buf, SCREEN_W, ATTR_TEXT, 0);
|
|
bios_write_until((const char *)raw_buf, SCREEN_W, 0);
|
|
}
|
|
|
|
static uint32_t raw_draw_wrap_row(uint32_t off, uint8_t row)
|
|
{
|
|
uint8_t i = 0;
|
|
uint32_t e = off, lim = off + SCREEN_W;
|
|
while (i < SCREEN_W && e < file_size) {
|
|
char c = fb(e);
|
|
if (c == '\n') {
|
|
raw_cell(i, 0);
|
|
break;
|
|
}
|
|
raw_cell(i, (uint8_t)(c == '\t' ? ' ' : c));
|
|
i++; e++;
|
|
}
|
|
raw_flush(row);
|
|
return raw_next_wrap(off);
|
|
}
|
|
|
|
static uint32_t raw_draw_hscroll_row(uint32_t off, uint8_t row)
|
|
{
|
|
uint32_t e = off;
|
|
while (e < file_size && fb(e) != '\n') e++; /* e = конец строки */
|
|
uint16_t len = e - off;
|
|
|
|
uint8_t i = 0;
|
|
uint32_t p = off + raw_hpan;
|
|
while (i < SCREEN_W && p < e) {
|
|
char c = fb(p);
|
|
raw_cell(i, (uint8_t)(c == '\t' ? ' ' : c));
|
|
i++; p++;
|
|
}
|
|
// raw_buf[i] = 0;
|
|
|
|
if (raw_hpan > 0)
|
|
raw_buf[0] = '<';
|
|
if (len > (uint16_t)raw_hpan + SCREEN_W)
|
|
raw_buf[SCREEN_W - 1] = '>';
|
|
if (i < SCREEN_W)
|
|
raw_buf[i] = 0;
|
|
raw_flush(row);
|
|
return (e < file_size) ? e + 1 : e;
|
|
}
|
|
|
|
/* Рисует один ряд от off (по режиму) и возвращает offset следующего ряда. */
|
|
static uint32_t raw_draw_one(uint32_t off, uint8_t row)
|
|
{
|
|
if (off >= file_size) { fill_row(row, ATTR_TEXT); return off; }
|
|
return (g_view == VIEW_RAW_WRAP) ? raw_draw_wrap_row(off, row)
|
|
: raw_draw_hscroll_row(off, row);
|
|
}
|
|
|
|
void raw_draw(void)
|
|
{
|
|
uint32_t off = raw_top;
|
|
for (uint8_t r = 0; r < VIEW_H; r++)
|
|
off = raw_draw_one(off, (uint8_t)(VIEW_TOP_ROW + r));
|
|
}
|
|
|
|
/* Прокрутка на одну строку: аппаратный scroll окна документа + отрисовка
|
|
* ОДНОЙ новой строки (как в MD-режиме). Смещение нижнего ряда вычисляется
|
|
* проходом VIEW_H рядов от raw_top — без отдельного инкрементального счётчика,
|
|
* который рассинхронизировался, когда контент кончается в середине экрана
|
|
* (короче окна после wrap→unwrap у конца файла). */
|
|
static uint32_t raw_next(uint32_t off)
|
|
{
|
|
return (g_view == VIEW_RAW_WRAP) ? raw_next_wrap(off) : raw_next_line(off);
|
|
}
|
|
static uint32_t raw_prev(uint32_t off)
|
|
{
|
|
return (g_view == VIEW_RAW_WRAP) ? raw_prev_wrap(off) : raw_prev_line(off);
|
|
}
|
|
|
|
/* ---- статус-бар: % по байтам (отрисовка — render_raw_status_numbers
|
|
* в mdview2_status.c, читает позицию через этот raw_pct) ---- */
|
|
|
|
/* Процент по байтам без 32-битного деления: масштабируем offset'ы (>>8),
|
|
* дальше 32-битная арифметика. raw_top приватен модулю, поэтому % считаем
|
|
* здесь и отдаём готовым числом в статус-модуль. */
|
|
uint8_t calc_raw_pct(void)
|
|
{
|
|
uint16_t a = (uint16_t)(raw_top >> 8);
|
|
uint16_t b = (uint16_t)(file_size >> 8);
|
|
if (b == 0) return 0; /* файл < 1 КБ */
|
|
return (uint8_t)(a * 100UL / b);
|
|
}
|
|
|
|
/* ---- посев позиции / пересев при смене активного буфера ----------- */
|
|
|
|
void raw_seed_from(uint16_t md_top_line)
|
|
{
|
|
uint32_t off = seg_off(md_top_line);
|
|
if (off > file_size) off = 0;
|
|
raw_top = raw_line_start(off);
|
|
raw_hpan = 0;
|
|
}
|
|
|
|
/* Сброс в начало — при смене активного буфера (F8 в/из UTF-8: размеры/offset'ы
|
|
* разные, старый raw_top невалиден). Без percent-математики. */
|
|
void raw_home(void)
|
|
{
|
|
raw_top = 0;
|
|
raw_hpan = 0;
|
|
}
|
|
|
|
void raw_end(void)
|
|
{
|
|
raw_top = file_size; /* отмотать VIEW_H рядов назад */
|
|
for (uint8_t k = 0; k < VIEW_H; k++)
|
|
raw_top = raw_prev(raw_top);
|
|
raw_hpan = 0;
|
|
}
|
|
|
|
void raw_screen_init(void) {
|
|
bios_clearwin(1, 0, 30, 80, ATTR_TEXT);
|
|
}
|
|
|
|
/* Нормализация позиции при смене под-режима RAW (F3 Wrap/Unwrap): выровнять
|
|
* raw_top на начало физической строки и снять горизонтальный сдвиг. */
|
|
void raw_renorm(void)
|
|
{
|
|
raw_top = raw_line_start(raw_top);
|
|
raw_hpan = 0;
|
|
}
|
|
|
|
void raw_scroll_down(uint8_t delta) {
|
|
uint32_t off = raw_top;
|
|
for (uint8_t r = 0; r < VIEW_H; r++) {
|
|
if (off >= file_size)
|
|
return; /* экран не заполнен контентом */
|
|
off = raw_next(off);
|
|
}
|
|
if (off >= file_size)
|
|
return; /* контент ровно по экран, ниже пусто */
|
|
|
|
if(delta ==1) {
|
|
scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 1, 0); /* содержимое вверх */
|
|
off = raw_draw_one(off, (uint8_t)(VIEW_TOP_ROW + VIEW_H - 1)); /* новый нижний ряд */
|
|
raw_top = raw_next(raw_top);
|
|
} else {
|
|
for (uint8_t k = 0; k < delta; k++) {
|
|
uint32_t n2 = raw_next(raw_top);
|
|
if (n2 >= file_size)
|
|
break;
|
|
raw_top = n2;
|
|
}
|
|
raw_draw(); /* PgUp/PgDn/Home/End — полный экран */
|
|
}
|
|
}
|
|
|
|
void raw_scroll_up(uint8_t delta) {
|
|
if (raw_top == 0)
|
|
return;
|
|
|
|
if (delta == 1) {
|
|
raw_top = raw_prev(raw_top);
|
|
scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 2, 0); /* содержимое вниз */
|
|
(void)raw_draw_one(raw_top, VIEW_TOP_ROW);
|
|
} else {
|
|
for (uint8_t k = 0; k < delta; k++)
|
|
raw_top = raw_prev(raw_top);
|
|
raw_draw(); /* PgUp/PgDn/Home/End — полный экран */
|
|
}
|
|
}
|
|
|
|
/* ---- навигация (вызывается из главного цикла при g_view != VIEW_MD) - */
|
|
|
|
uint8_t raw_key(uint8_t scan)
|
|
{
|
|
switch (scan) {
|
|
case KEY_DOWN:
|
|
raw_scroll_down(1);
|
|
break;
|
|
case KEY_UP:
|
|
raw_scroll_up(1);
|
|
break;
|
|
case KEY_PGDN:
|
|
raw_scroll_down(VIEW_H);
|
|
break;
|
|
case KEY_PGUP:
|
|
raw_scroll_up(VIEW_H);
|
|
break;
|
|
case KEY_HOME:
|
|
raw_home();
|
|
raw_draw();
|
|
break;
|
|
case KEY_END:
|
|
raw_end();
|
|
raw_draw();
|
|
break;
|
|
case KEY_LEFT:
|
|
if (g_view == VIEW_RAW_HSCROLL && raw_hpan) {
|
|
raw_hpan = (raw_hpan >= HPAN_STEP) ? (uint16_t)(raw_hpan - HPAN_STEP) : 0;
|
|
raw_draw();
|
|
}
|
|
break;
|
|
case KEY_RIGHT:
|
|
if (g_view == VIEW_RAW_HSCROLL && raw_hpan < 248u) {
|
|
raw_hpan = (uint16_t)(raw_hpan + HPAN_STEP);
|
|
raw_draw();
|
|
}
|
|
break;
|
|
default:
|
|
return 0; /* не наша клавиша */
|
|
}
|
|
|
|
// render_raw_status_numbers();
|
|
render_percent_progress(calc_raw_pct());
|
|
return 1;
|
|
}
|
|
|
|
#endif /* WITH_RAW */
|