Перейти к содержимому

WIN320 — оконный интерфейс

WIN320.DLL — библиотека простого оконного интерфейса для Sprinter в режиме #81 (320×256, 256 цветов). Это не фреймворк: библиотека даёт набор готовых блоков — панели, кнопки, поля ввода, списки, скроллбары, прогрессбары, иконки, чекбоксы и радиокнопки, — из которых приложение на ассемблере, C (SDCC) или Turbo Pascal собирает интерфейс в стиле GFX Viewer / Flex Navigator. Рисует всё аппаратный акселератор Sprinter, но это деталь реализации, а не часть интерфейса.

Общие правила загрузки DLL — в обзоре библиотек.

  • Декларативный — контролы описываются структурами, ссылки на них собираются в массив WinItem[], массив — в окно WinWindow. Окно рисуется одним вызовом win_draw, а дальше обновляется точечно: приложение меняет состояние контрола, ставит флаг WIN_IT_DIRTY и вызывает win_update — перерисовываются только изменённые элементы.
  • Императивный — те же структуры передаются напрямую в win_button, win_panel, win_label и т. д., без окна и массивов.

Структуры в обоих путях одинаковы: описание кнопки из окна без изменений работает и в прямом вызове.

include "win320.inc"
ld bc,#0050 ; DSS SETVMOD
ld a,#81 ; режим 320x256x256 включает приложение
rst #10
ld hl,dll_name ; "WIN320.DLL",0
ld a,3 ; DLL — в окно WIN3
call LIBMAN.l_load
jp c,load_error
ld (dll_handle),hl ; шрифт уже загружен из хвоста DLL
; палитра GUI + очистка экрана цветом рабочего стола
ld e,WIN_STYLE_PALETTE|WIN_STYLE_CLEAR
ld d,#FF ; #FF = цвет рабочего стола из темы
ld b,WIN_STYLE
call api
jr nz,win_error
...
api: ld hl,(dll_handle)
call LIBMAN.l_call ; аргументы: A, DE, IX, IY
ret c ; CF — ошибка диспетчера libman
or a ; ZF по статусу WIN320
ret

Разбор: приложение включает режим само, загружает DLL в WIN3 (код, данные и стек лучше держать в WIN0–WIN2) и первым делом вызывает win_style — тот ставит палитру GUI и заливает экран. Без палитры любой цвет выводится чёрным, поэтому этот вызов обязателен. Обёртка api — типовой приём: один раз пишем проверку двух уровней ошибок (CF диспетчера и статус в A, 0 — успех, #20..#28 — коды WIN_ERR_*).

Каждая рисуемая структура начинается одинаковым заголовком: x, y, width, height. Координаты отсчитываются от начала отсчёта — по умолчанию это угол экрана, а при рисовании окна — угол окна. Поэтому диалог перемещается правкой двух полей WinWindow.x/y, без пересчёта координат контролов.

Цвета почти никогда не задаются явно: значение #FF в любом цветовом поле означает «взять из темы». Тема — 18 цветовых ролей (свет и тень 3D-рамок, тело панелей, фон рабочего стола, текст, поле ввода, выделение, заголовок окна…), задаётся одним вызовом win_set_theme — и весь интерфейс перекрашивается.

window: dw 32,20,256,216 ; x, y, ширина, высота
db #FF ; цвет тела — из темы
db 0 ; флаги окна
db 7 ; число объектов
db 2 ; индекс объекта с фокусом
dw items ; массив WinItem[]
dw title ; заголовок (0 — без полосы заголовка)
db #FF ; цвет заголовка — из темы
db 0 ; служебное поле
items: ; 8 байт на запись: тип, флаги, id, указатель, user_data
db WIN_T_BUTTON, WIN_IT_HIT|WIN_IT_FOCUSABLE|WIN_IT_PRESS
db ID_OK
dw ok_button ; -> структура WinButton
dw 0
...

win_draw рисует окно целиком; win_update перерисовывает только записи с флагом WIN_IT_DIRTY (и снимает его). Типовой цикл: обработать событие → изменить состояние контрола → поставить dirty → win_update.

Окно может иметь заголовок с кнопкой закрытия (WinWindow.title + флаг WIN_WND_CLOSE) — полоса рисуется автоматически, а полный клик по кнопке закрытия приходит событием WIN_EV_CLOSE.

win_open сохраняет фон под окном в страницы памяти, выделенные приложением (win_set_backstore), и рисует окно; win_close восстанавливает фон. Стек до четырёх вложенных окон, строго «последним открыт — первым закрыт». При ошибке в списке объектов win_open откатывается атомарно — частично нарисованный диалог на экране не остаётся.

Два способа опроса, оба через структуру WinTrack (принадлежит приложению):

  • win_poll — одна неблокирующая итерация: события нет — вернулся WIN_EV_NONE, можно заняться фоновой работой;
  • win_track — блокирует до события (с флагом WIN_TRK_HALT — экономно, через HALT).

События: клики левой/правой кнопкой, автоповтор, hover/уход курсора, горячие клавиши из таблицы WinKey[], произвольные клавиши, смена фокуса, изменение чекбокса/радио, клик мимо всех объектов, кнопка закрытия окна. Вместе с событием приходят индекс и id объекта, подобласть (например, стрелка скроллбара или строка списка) и координаты мыши.

Фокус клавиатуры обслуживает библиотека: Tab/Shift+Tab обходят объекты с флагом WIN_IT_FOCUSABLE, Enter нажимает сфокусированную кнопку, Space переключает чекбокс, стрелки ходят по радиогруппе.

Драйвер мыши инициализирует приложение (BIOS RST #30, C=0) — библиотека только опрашивает его и управляет курсором.

  • Поле ввода (win_edit) — модальное редактирование прямо в буфере приложения: курсор, Backspace/Delete, Home/End, переход по словам Ctrl+←/→, откат по Esc, позиционирование кликом, режим пароля.
  • Listbox — строки либо массивом указателей, либо в страницах памяти (до 5461 записи); перерисовка дифференциальная, сдвиг на строку — быстрым аппаратным скроллом.
  • Скроллбар — сам пересчитывает ползунок из first/visible/total; стрелки обслуживаются контролом, hit-test сообщает подобласть.
  • Прогрессбар — единица — процент; перерисовывается только разница.
  • Иконки — 8×8 и 16×16 из страниц памяти приложения, с аппаратной прозрачностью #FF; упаковщик winiconpack.py собирает их из PNG/BMP.
  • Чекбокс и радиокнопки — состояние, эксклюзивность группы, реакция на мышь и клавиатуру — всё внутри библиотеки, приложению приходит готовое WIN_EV_CHANGE.
static win_button_t ok = {40, 154, 72, 20, 0xff, 0, 0}; /* x,y,w,h,attr,flags,text */
static win_item_t items[7];
static win_window_t window = {32, 20, 256, 216, 0xff, 0, 7, 2, 0, 0xff, 0};
static win_track_t track;
win320_bind(handle); /* привязать обёртки к DLL */
win320_set_text_format(WIN_TXT_ASCIIZ);
win320_draw(&window); /* нарисовать окно целиком */
for (;;) {
if (win320_poll(&track)) break; /* ошибка ABI */
if (track.event == WIN_EV_NONE) continue;/* фоновая работа здесь */
if (track.id == ID_CANCEL) break;
items[STATUS_INDEX].flags |= WIN_IT_DIRTY;
win320_update(&window, &drawn); /* перерисовать только dirty */
}

В поставке один и тот же диалог реализован трижды — dialog.asm, dialog.c и DIALOG.PAS — удобно сравнивать языки. Для Turbo Pascal есть записи и хелперы LibCall, а формат строк переключается на «байт длины» вызовом win_set_text_format, так что поля ввода работают прямо со string[n].

  • Приложение само включает режим #81 и само переключает видимый экран; библиотека умеет рисовать в любой из двух буферов (win_set_screen).
  • Данные вызова не должны лежать в окне DLL: на время вызова там подключена сама библиотека. Изменяемые структуры (окно, буфер редактирования, WinTrack) — в записываемой RAM, в C их нельзя объявлять const.
  • Библиотека нереентерабельна: не вызывать из обработчиков прерываний.
  • Не печатать через консоль DSS в графическом режиме (см. обзор).
Файл Назначение
WIN320.DLL библиотека; шрифт вшит в хвост файла
win320.inc константы и смещения полей для sjasmplus
bind/win320.h, bind/win320.lib заголовок и обёртки для C (SDCC)
bind/WIN320.INC записи и хелперы для Turbo Pascal
examples/dialog.asm, dialog.c, DIALOG.PAS один диалог на трёх языках
tools/winiconpack.py упаковщик иконок PNG/BMP
WIN320.EXE визуальный тест; приложениям не нужен