Skip to content

rigidus/macforth

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

44 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Libs

MacOs

brew install sdl2_ttf

Ubuntu:

sudo apt install libsdl2-ttf-dev

Windows (MSYS2)

pacman -S mingw-w64-x86_64-SDL2_ttf

Targets

Web

Перед сборкой: установи и активируй Emscripten SDK (один раз):

git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh

emcc пытается скачать/распаковать порт FreeType в системный кэш usr/share/emscripten/cache/ports, куда может не быть прав. Нужно направить кэш Emscripten в каталог, где можно писать. Это делает =wasm-setup=, при “замороженном” кэше emcc не имеет права ничего докачивать и требует уже готовый кэш. Разморозив его и указав путь, ты позволяешь emcc один раз скачать и собрать нужные порты в ~.emscripten_cache. Затем сборки make wasm пойдут офлайн.

make wasm-setup
make wasm
make serve
firefox 127.0.0.1:8080

Files

/src
  platform/         // всё общение с SDL/OS
    platform_sdl.c
    platform_sdl.h
  gfx/              // рендер и 2D-утилиты поверх SDL_Surface (но без SDL_Window!)
    surface.c surface.h
    text.c    text.h    // TTF, но наружу — только text_* API
  core/             // независимое "ядро" WM (про окна, z-order, focus, damage)
    wm.c    wm.h
    window.c window.h   // интерфейс окна: vtable, базовые хелперы
    damage.c damage.h
    timing.c timing.h
    input.c  input.h    // распределение событий по окнам, многопользовательский фокус
  apps/             // конкретные типы окон (демо/виджеты)
    win_paint.c   win_paint.h
    win_square.c  win_square.h
    win_console.c win_console.h
main.c
/include            // публичные заголовки

Слои и их роли

platform/ (SDL-обвязка)

  • Создаёт окно ОС и SDL_Surface окна (screen).
  • Держит собственный ARGB32 backbuffer (pf->back).
  • Конвертирует SDL-события в абстрактные InputEvent и вызывает маршрутизатор ввода.
  • Собирает кадр из окон в backbuffer и копирует нужные регионы в screen, затем делает SDL_UpdateWindowSurface*.
  • В wasm: вместо бесконечного while — emscripten_set_main_loop.

core/ (ядро WM, без SDL-типа окна)

  • Window + WindowVTable — интерфейс каждого окна (draw/on_event/tick/on_focus/destroy).
  • WM — список окон, сортировка по z, поиск topmost, фокус по пользователям, общий DamageList, размеры экрана.
  • input.c — маршрутизация ввода: выбор целевого окна, перевод координат в локальные, фиксация перетаскивания, постановка damage (включая старый и новый рямоугольник при drag).
  • timing.h — FRAME_MS и мелкие хелперы.

gfx/ (графика-помощники)

  • surface.* — тонкая обёртка над SDL_Surface (ARGB32): fill, fill_rect, blit, checkerboard.
  • text.* — инициализация TTF, рендер строки в Surface, измерение «ширина/высота».
  • apps/ (реальные окна)
  • win_paint — фон+рисование по клику/drag.
  • win_square — цветной квадрат с анимацией и drag.
  • win_console — история строк (кольцевой буфер), редактируемая строка, мигающий курсор.

main.c

  • Склеивает всё: создаёт платформу → инициализирует текст → создаёт WM → добавляет окна → запускает цикл (native или wasm).

Поток событий (Input → Window)

  • SDL генерит событие → platform переводит его в InputEvent.
  • input_route_*():
    • Для мыши: по LMB-down выбирается topmost окно под курсором, делается bring-to-front, фокус для пользователя.
    • Перед on_event: сохраняет старый frame.
    • Вызывает window->vt->on_event(window, e, lx, ly).
    • После on_event: если окно сдвинулось/изменилось, добавляет damage старого и нового прямоугольников, помечает invalid_all=true. Если окно само пометило ебя грязным — добавляет damage его текущей области.
  • Для клавиатуры/текста: событие отправляется в сфокусированное окно соответствующего пользователя.

Рендеринг (Damage → Backbuffer → Screen)

  • WM копит DamageList — список прямоугольников, которые надо обновить.
  • platform.plat_compose_and_present:
    • При необходимости дожидается следующего кадра (≤ 60 Гц).
    • Если damage пуст, но есть анимации — берёт весь экран как damage (чтобы реально перерисовать).
    • Для каждого damage-региона:
      • Очищает фон в backbuffer (черный).
      • Идёт по окнам снизу-вверх: % Если invalid_all, вызывает window->vt->draw() (окно обновляет свой кэш w->cache целиком). % Пересекает регион с w->frame и blit-ит соответствующую часть w->cache в backbuffer.
      • Копирует готовый регион из backbuffer в screen (SDL_BlitSurface).
    • Вызывает SDL_UpdateWindowSurfaceRects (или SDL_UpdateWindowSurface для полноэкрана).
    • Очищает DamageList.

Так обеспечиваются:

  • частичная перерисовка (только нужные регионы),
  • корректная композиция по z-index,
  • отсутствие «шлейфов» (старое и новое положение окна попадают в damage).

Анимации и тайминг

  • Окно, у которого есть активная динамика, ставит w->animating=true и планирует w->next_anim_ms.

wm_tick_animations(now) вызывает w->vt->tick для нужных окон; те помечают себя invalid_all и добавляют damage (обычно область окна), а также переустанавливают next_anim_ms.

  • Платформа делает мягкий pacing к FRAME_MS (≈16 мс).

Фокус и мультипользовательский режим

  • WM хранит focus[user_id] → Window*.
  • Клик левой кнопкой мыши привязывает верхнее окно под курсором к фокусу соответствующего пользователя и поднимает его на передний план.
  • Клавиатура/текст идут в текущее окно фокуса. При смене — вызывается on_focus(true/false).

Как устроены окна (пример)

Каждое окно:

  • имеет собственный кэш Surface* cache размера frame.w x frame.h (локальные координаты);
  • реализует vtable:
    • draw(w, invalid) — рисует в свой кэш;
    • on_event(w, e, lx, ly) — обрабатывает ввод в локальных координатах;
    • tick(w, now) — обновляет состояние и просит следующий кадр;
    • destroy(w) — освобождает w->user и ресурсы, специфичные для окна.
  • всё специфическое состояние лежит в w->user (malloc-нутый struct), ядро его не знает.

Особенности каждой «апки»

  • win_paint: рисует фон (checkerboard) один раз; по клику/drag пишет пиксель в свой cache и помечает invalid_all → ядро перенесёт нужный регион на экран.
  • win_square: tick меняет цвет квадрата по косинусному закону, делает invalidate окна каждый кадр анимации; on_event по клику в квадрат разворачивает фазу; drag — меняет frame, но всю грязь и правильный repaint делает централизованный input_route_mouse.
  • win_console: хранит историю строк (кольцевой буфер), редактируемую строку, мигает курсором. По Enter — кладёт edit в историю; отрисовывает rows-1 оследних строк + текущую (нижнюю).

Жизненный цикл

  • main:
    • plat_create → text_init → wm_create.
    • Создаёт и добавляет окна.
    • Ставит начальный damage всего экрана.
  • Цикл:
    • plat_poll_events_and_dispatch → wm_tick_animations (если нужно) → plat_compose_and_present.
  • Завершение:
    • wm_destroy (вызовет destroy у окон и освободит их кэши) → text_shutdown → plat_destroy.

Общее и чистота

  • Все модули видят только свои заголовки (window.h, wm.h, surface.h, text.h).
  • SDL-детали заперты в platform/ и gfx/.
  • Окна — изолированы: они не знают про SDL и ядро, только про Window, Rect, InputEvent, Surface.
  • Менять/добавлять окна можно, не трогая платформу/ядро.
  • apps/* не тянут platform/* и не включают SDL-типы.
  • core/* не знает про SDL_Window/SDL_Event — только свои абстракции (InputEvent, Rect, Surface*).
  • platform/* — единственное место, где живут SDL_Window, события SDL и копирование в окно.
  • gfx/* — единственный слой, знающий про SDL_Surface/SDL_ttf; наружу отдаёт Surface*.

Зависимости

Карта модулей (крупно)

  • apps/* → core/window.h, core/wm.h, gfx/surface.h, gfx/text.h
  • core/* → (внутри core) window.h, wm.h, damage.h, input.h, timing.h + иногда gfx/surface.h
  • platform/platform_sdl.* → core/wm.h, core/input.h, core/timing.h, gfx/surface.h + SDL
  • gfx/surface.* → SDL (Surface-утилиты)
  • gfx/text.* → SDL_ttf (+ SDL), возвращает Surface* (gfx/surface.h)
  • main.c → всё публичное: platform/platform_sdl.h, core/wm.h, core/window.h, apps/*, gfx/text.h

Пофайлово (точно)

  • main.c
    • platform/platform_sdl.h
    • core/wm.h, core/window.h
    • apps/win_paint.h, apps/win_square.h, apps/win_console.h
    • gfx/text.h
  • src/platform/platform_sdl.c
    • platform/platform_sdl.h (собственный заголовок)
    • core/wm.h, core/input.h, core/timing.h
    • gfx/surface.h
    • <SDL.h> (и косвенно SDL_ttf через text в рантайме, но прямо не включает)
  • src/core/wm.c
    • core/wm.h
    • gfx/surface.h (для освобождения кэшей окон)
  • src/core/input.c
    • core/input.h, core/wm.h
  • src/core/window.c
    • core/window.h
    • gfx/surface.h (создание/заливка кэша окна)
  • src/core/damage.c
    • core/damage.h (inline-реализация в .h)
  • src/core/timing.h
    • без внешних зависимостей
  • src/gfx/surface.c
    • gfx/surface.h
    • <SDL.h>
  • src/gfx/text.c
    • gfx/text.h
    • <SDL_ttf.h>, <SDL.h>
    • gfx/surface.h (создаёт Surface из SDL_Surface)
  • src/apps/win_paint.c
    • apps/win_paint.h
    • gfx/surface.h
    • core/wm.h (для invalidate, Rect)
  • src/apps/win_square.c
    • apps/win_square.h
    • gfx/surface.h
    • core/wm.h, core/timing.h
    • <math.h>
  • src/apps/win_console.c
    • apps/win_console.h
    • gfx/surface.h, gfx/text.h
    • core/timing.h
    • <SDL.h> *(для SDL_GetTicks), <string.h>, <stdlib.h>

Диаграмма зависимостей

@startuml
title Dependency graph – Cross WM

skinparam packageStyle rectangle
skinparam ArrowColor #888
skinparam packageTitleFontColor #333
skinparam defaultTextAlignment left

package "main" as main {
    [main.c]
}

package "platform" as platform {
    [platform_sdl.h]
    [platform_sdl.c]
}

package "core" as core {
    [window.h]
    [window.c]
    [wm.h]
    [wm.c]
    [damage.h]
    [damage.c]
    [input.h]
    [input.c]
    [timing.h]
}

package "gfx" as gfx {
    [surface.h]
    [surface.c]
    [text.h]
    [text.c]
}

package "apps" as apps {
    [win_paint.h]
    [win_paint.c]
    [win_square.h]
    [win_square.c]
    [win_console.h]
    [win_console.c]
}

package "3rd-party" as thirdparty {
    [SDL2]
    [SDL2_ttf]
}

' ---- Dependencies ----
main --> platform
main --> core
main --> apps
main --> gfx

platform --> core
platform --> gfx
platform --> [SDL2]

core --> gfx

apps --> core
apps --> gfx

gfx --> [SDL2]
gfx --> [SDL2_ttf]

legend right
    Arrows show "uses public API of".
    core never includes SDL types.
    apps know only Window/WM/Input and gfx::Surface/Text.
    platform is the only module touching SDL_Window & event loop.
    gfx wraps SDL_Surface/SDL_ttf and exposes Surface/Text API.
endlegend

@enduml

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published