/*
Pointer Basics: read mouse position, buttons, movement delta, and the scroll wheel.
@description Read mouse position, movement delta, scroll wheel, and four pointer buttons, with a live crosshair.
Part of the BLIT386 demo series.
Prerequisites:
- Basics: https://demos.blit386.dev/basics
- Primitives: https://demos.blit386.dev/primitives
Live version: https://demos.blit386.dev/pointer-basics
Guides:
- Input: https://blit386.dev/docs/guides/input#position-and-delta
Reference:
- Core: https://blit386.dev/docs/api/core#hardware-settings
---
WHAT YOU WILL SEE
-----------------
A dark screen with a title strip across the top and two equal panels just below it. On
the left, live readouts for pointer slot 0 - whether it is active, its position, how far
it moved this frame, how far the wheel turned, and a meter that fills as you scroll
down. On the right, four pips labeled A to D that light up while the matching mouse
button is held, and a count of the touch slots (1-3) in use.
A white crosshair follows your mouse (or your finger), trailing a cyan line through its
last 24 positions. The crosshair stays on top of the panels; the trail passes behind
them. While no pointer is over the canvas, a dim "Move pointer over canvas" hint sits in
the middle of the open space under the panels instead.
WHAT YOU WILL LEARN
-------------------
- The pointer slot model: slot 0 is always the mouse, slots 1-3 are fingers (or a pen) in
the order they touched down.
- BT.isPointerActive(slot) says whether a slot has a live pointer right now.
- BT.pointerPos(slot) and BT.pointerDelta(slot) return the position and the movement
since the last update, both in display pixels.
- BT.isDown(BT.BTN_POINTER_A, 0) and friends read the four mouse buttons: A is left, B is
right, C is middle, D is the back or forward button. Touch slots only ever report A.
- BT.pointerScrollDelta is the wheel movement for the current frame. It stays 0 unless
configure() sets isCapturingPointerScroll: true.
- BT.hideCursor() hides the OS cursor so a drawn crosshair can take its place.
- Pointer state is held state, not an edge, so reading it from render() is safe.
TRY THIS
--------
- Move the mouse over the demo: the crosshair and its trail follow slot 0.
- Hold the left, right, or middle button to light up the A, B, or C pip. A mouse with a
back or forward button lights up D.
- Spin the scroll wheel to fill or empty the "Scroll pos" meter.
- On a touchscreen, drag a finger to move the crosshair on slots 1-3 (slot 0 stays
reserved for the mouse). The wheel and the extra buttons have no touch equivalent, so a
one-line note appears in the bottom-right corner once a touch is seen. Pointer Paint
(https://demos.blit386.dev/pointer-paint) uses all four slots at once.
- Press the backquote key (`) to open the engine overlay and watch the timing chart.
*/
import { function bootstrap(DemoClass: DemoConstructor, options?: BootstrapOptions): Promise<boolean>One-liner bootstrap function for BLIT386 demos.
Handles canvas retrieval and engine initialization. Backend selection
(WebGPU or software fallback) is managed internally by BTAPI.
This function provides a streamlined way to start a demo with sensible defaults
while allowing customization through options.bootstrap, const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT, class Color32Mutable 32-bit RGBA color value with 8-bit channels.Color32, class Rect2iInteger rectangle for pixel-perfect bounds and regions.
Used throughout the engine for sprite regions, display-space bounds, and
zero-allocation geometry helpers. Both convenience getters and allocation-free
`*To()` helpers are provided so callers can choose between readability and
hot-path efficiency.Rect2i, class Vector2iInteger 2D vector for pixel-perfect positioning.
Used for points, sizes, directions, and camera offsets throughout the engine.
The API includes both allocation-free `*To()` / `*InPlace()` variants and
convenience methods that return new vectors.Vector2i } from 'blit386';
import { import applyThemeapplyTheme, import FONT_WFONT_W, import uiui, import UI_ANCHORSUI_ANCHORS } from './shared/ui.js';
/** @typedef {import('blit386').Palette} Palette */
// Scene palette slots. Slot 0 is always transparent. All the UI colors (panels, text,
// meter fill) come from the shared theme installed by applyTheme() in init(), so the demo
// only needs slots for its own artwork: the crosshair and the cyan trail behind it.
const const C_CROSSHAIR: 1C_CROSSHAIR = 1; // white crosshair that follows the pointer
const const C_TRAIL: 2C_TRAIL = 2; // cyan trail line behind the crosshair
// Number of past positions remembered for the cursor trail. Each tick we shift in the
// latest position and draw a line through all of them.
const const TRAIL_LENGTH: 24TRAIL_LENGTH = 24;
// Multiplier applied to the raw scroll delta (BT.pointerScrollDelta) before it is added
// to the scroll position. The browser reports scrolling in CSS pixels, which adds up fast
// - shrinking each report to a quarter keeps one wheel click moving the meter a few
// pixels instead of a big jump. update() then clamps the scroll position to the display
// height so the meter fill always stays between empty and full.
const const SCROLL_SENSITIVITY: 0.25SCROLL_SENSITIVITY = 0.25;
// Layout: two equal panels, 152 pixels wide and 8 apart, sit 4 pixels below the
// 22-pixel title strip and 4 pixels from the screen sides (4 + 152 + 8 + 152 + 4 = 320).
// Both reserve 63 pixels of rows, so they end on the same line. Everything below them is
// open space for the crosshair.
const const PANEL_Y: 26PANEL_Y = 26;
const const READOUTS_W: 152READOUTS_W = 152;
const const BUTTONS_W: 152BUTTONS_W = 152;
const const PANELS_BOTTOM: numberPANELS_BOTTOM = const PANEL_Y: 26PANEL_Y + 22 + 63 + 11;
// Half the length of each crosshair arm, in pixels.
const const CROSSHAIR_ARM: 6CROSSHAIR_ARM = 6;
// Demonstrates the basic pointer API: position, delta, scroll wheel, and the four pointer
// buttons (A, B, C, D) on slot 0 (the mouse).
class class DemoDemo {
/** @type {Palette | null} */
Demo.palette: Palette | nullpalette = null;
// Palette slot map returned by applyTheme() - theme.bg, theme.dim, and so on. Filled
// in init(); used for the screen clear.
Demo.theme: nulltheme = null;
// Recent positions, oldest first. Each tick we push the current position and drop the
// oldest, so the trail shows the cursor's recent path. Each entry is [x, y].
Demo.trail: {}trail = [];
// Reused for the two crosshair arms and the two ends of each trail segment, so
// drawing allocates no new objects.
Demo.armRect: Rect2iarmRect = new new Rect2i(x?: number, y?: number, width?: number, height?: number): Rect2iCreates an integer rectangle, truncating all inputs toward zero.Rect2i(0, 0, 0, 0);
Demo.lineFrom: Vector2ilineFrom = new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(0, 0);
Demo.lineTo: Vector2ilineTo = new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(0, 0);
// Accumulated scroll position in display pixels. Centered in init() from
// BT.displaySize. BT.pointerScrollDelta pushes it up or down, and the scroll meter in
// the readouts panel shows it as a 0..1 fill.
Demo.scrollPos: numberscrollPos = 0;
// Turns on wheel capture and the overlay timing chart.
Demo.configure(): {
isCapturingPointerScroll: boolean;
isOverlayEnabled: boolean;
}
configure() {
return {
// Opt into canvas wheel capture so BT.pointerScrollDelta works and the page
// does not scroll while the pointer is over the demo.
isCapturingPointerScroll: booleanisCapturingPointerScroll: true,
isOverlayEnabled: booleanisOverlayEnabled: false,
};
}
// Runs once at startup. Sets up the palette and prefills the trail.
async Demo.init(): unknowninit() {
this.Demo.palette: Palette | nullpalette = const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.paletteCreate: (size?: number) => PaletteCreates a standalone palette instance.paletteCreate(256);
// Scene colors: just the crosshair and its trail. Everything else on screen
// (panels, labels, the meter) is drawn by the shared UI kit.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_CROSSHAIR: 1C_CROSSHAIR, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(255, 255, 255));
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_TRAIL: 2C_TRAIL, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(80, 200, 255));
// Install the shared UI colors (slots 240-251) and keep the slot map so we can
// clear with the theme background and reuse the dim text color for the hint.
this.Demo.theme: nulltheme = import applyThemeapplyTheme(this.Demo.palette: Palettepalette);
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.paletteSet: (palette: Palette) => voidStores the active engine palette.
Use this to swap the **entire palette** (e.g. switch between a day and night
theme). After this call the renderer uploads the new palette uniform on the
next frame.
**Palette-value swap (change what a slot looks like):** mutate the live
{@link
BT.palette
}
in place with `palette.set(slot, newColor)`. The renderer
uploads dirty slots on the next frame; no `paletteSet()` or
{@link
BT.spritesRefresh
}
needed.
**Palette-layout swap (same colors, different slot positions):** build a new
palette with the same colors at new indices, call `paletteSet()`, then call
{@link
BT.spritesRefresh
}
so every sprite sheet re-maps its original RGBA
pixels against the new slot layout.paletteSet(this.Demo.palette: Palettepalette);
const const screen: Vector2iscreen = const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.displaySize: Vector2iActive logical render resolution in pixels.
This is the game/simulation coordinate space configured by the demo, not
the canvas element's CSS size. Each read returns a clone.displaySize;
this.Demo.scrollPos: numberscrollPos = Math.floor(const screen: Vector2iscreen.Vector2i.y: numberVertical component (defaults to 0).y / 2);
// Hide the native OS cursor so the drawn crosshair is the only cursor visible
// while the pointer is over the canvas.
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.hideCursor: () => voidHides the native OS cursor while the pointer is over the canvas.
Call once from `init()` when the demo draws its own crosshair or
cursor sprite in place of the system arrow. The cursor is restored
automatically when the engine shuts down.
No-op before the engine is initialized.hideCursor();
// Prefill the trail with the center point so the very first frame has something
// to draw without a special-case "no history yet" path.
for (let let i: numberi = 0; let i: numberi < const TRAIL_LENGTH: 24TRAIL_LENGTH; let i: numberi++) {
this.Demo.trail: {}trail.push([Math.floor(const screen: Vector2iscreen.Vector2i.x: numberHorizontal component (defaults to 0).x / 2), Math.floor(const screen: Vector2iscreen.Vector2i.y: numberVertical component (defaults to 0).y / 2)]);
}
return true;
}
// Per-tick update: push the current pointer position into the trail and apply the
// scroll-wheel delta to the accumulated scroll position.
Demo.update(): voidupdate() {
// Let the UI kit do its per-tick housekeeping first. This demo asks the kit
// whether a touchscreen has been used (ui.hasTouch() in render()), and that
// answer is kept fresh here.
import uiui.tick();
// Only record the trail when a pointer is over the canvas. The mouse uses slot 0;
// touches use slots 1-3. If none are active, keep the previous trail intact so
// the line does not snap to (0, 0).
const const slot: numberslot = this.Demo.activePointerSlot(): numberactivePointerSlot();
if (const slot: numberslot >= 0) {
const const pos: Vector2ipos = const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.pointerPos: (pointerIndex?: number) => Vector2iReturns the position of the pointer in the given slot, in display coordinates.
Slot 0 is the mouse; slots 1 through 3 are touch / pen contacts assigned
in arrival order. Returns `Vector2i.zero()` when the engine has not been
initialized, the slot index is out of `[0, 3]`, or the slot has no live
pointer.pointerPos(const slot: numberslot);
// Drop the oldest sample and append the new one.
this.Demo.trail: {}trail.shift();
this.Demo.trail: {}trail.push([const pos: Vector2ipos.Vector2i.x: numberHorizontal component (defaults to 0).x, const pos: Vector2ipos.Vector2i.y: numberVertical component (defaults to 0).y]);
}
// Convert the scroll delta (CSS pixels of scrolling) into a small movement.
// Multiplying by a fraction makes one wheel click move the meter a few pixels
// instead of jumping a full screen height.
this.Demo.scrollPos: numberscrollPos += const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.pointerScrollDelta: numberWheel scroll delta accumulated during the current frame, in pixels.
Aggregates `WheelEvent.deltaY` across all wheel events received since
the last frame, normalizing line and page delta modes to pixels.
Requires
{@link
HardwareSettings.isCapturingPointerScroll
}
(or overlay
palette-band force capture); otherwise this stays `0` and the host page
scrolls normally.pointerScrollDelta * const SCROLL_SENSITIVITY: 0.25SCROLL_SENSITIVITY;
// Keep the scroll position inside the visible range, using the display height
// rather than a hard-coded 240.
const const maxScrollY: numbermaxScrollY = const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.displaySize: Vector2iActive logical render resolution in pixels.
This is the game/simulation coordinate space configured by the demo, not
the canvas element's CSS size. Each read returns a clone.displaySize.Vector2i.y: numberVertical component (defaults to 0).y;
if (this.Demo.scrollPos: numberscrollPos < 0) {
this.Demo.scrollPos: numberscrollPos = 0;
} else if (this.Demo.scrollPos: numberscrollPos > const maxScrollY: numbermaxScrollY) {
this.Demo.scrollPos: numberscrollPos = const maxScrollY: numbermaxScrollY;
}
}
// Per-frame render: clear, draw the UI kit panels (readouts and button pips), then
// the cursor trail and the crosshair. The trail is plain lines, which the engine
// draws before every sprite, so it passes behind the panels; the crosshair is drawn
// as kit sprites after the panels, so the "cursor" is never hidden behind one.
Demo.render(): voidrender() {
// Clear to the shared theme background so this demo matches the rest of the
// series.
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.clear: (paletteIndex: number) => voidSets the frame clear color using a palette index.
The renderer uses this color when clearing the full display at the start
of the next frame.clear(this.Demo.theme: nulltheme.bg);
// Full-width title strip with the one-line instructions.
import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_BAR);
import uiui.panel('Pointer Basics - move, click, spin the wheel');
import uiui.end();
this.Demo.renderReadouts(): voidrenderReadouts();
this.Demo.renderButtonPips(): voidrenderButtonPips();
this.Demo.renderTouchNote(): voidrenderTouchNote();
this.Demo.renderTrail(): voidrenderTrail();
this.Demo.renderCrosshair(): voidrenderCrosshair();
this.Demo.renderPointerHint(): voidrenderPointerHint();
}
// Readouts panel in the top-left: pointer position, delta, wheel delta, whether slot
// 0 is active, and a meter showing the accumulated scroll position. The raw
// BT.pointer* reads here are the whole point of the demo - pointer state (unlike
// keyboard edges) is safe to read from render().
Demo.renderReadouts(): voidrenderReadouts() {
// Is the mouse currently over the canvas?
const const active: booleanactive = const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.isPointerActive: (pointerIndex?: number) => booleanReports whether the given pointer slot has a live pointer.
For slot 0 (mouse) this is true while the mouse is hovering inside the
canvas; cleared on `pointerleave`. For slots 1-3 (touch / pen) this is
true while the contact is down.isPointerActive(0);
// How far the wheel moved this frame (positive = scrolling down).
const const scroll: numberscroll = const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.pointerScrollDelta: numberWheel scroll delta accumulated during the current frame, in pixels.
Aggregates `WheelEvent.deltaY` across all wheel events received since
the last frame, normalizing line and page delta modes to pixels.
Requires
{@link
HardwareSettings.isCapturingPointerScroll
}
(or overlay
palette-band force capture); otherwise this stays `0` and the host page
scrolls normally.pointerScrollDelta;
// The panel starts just below the title strip, at a fixed width so its edge does
// not jump while the numbers in it change length.
import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_LEFT, { y: numbery: const PANEL_Y: 26PANEL_Y, width: numberwidth: const READOUTS_W: 152READOUTS_W });
import uiui.panel('Slot 0 (mouse)');
import uiui.kv('Active', const active: booleanactive ? 'yes' : 'no');
// Only read position and delta when the pointer is over the canvas. The engine
// keeps the last known values after the mouse leaves, so they would be stale.
if (const active: booleanactive) {
const const pos: Vector2ipos = const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.pointerPos: (pointerIndex?: number) => Vector2iReturns the position of the pointer in the given slot, in display coordinates.
Slot 0 is the mouse; slots 1 through 3 are touch / pen contacts assigned
in arrival order. Returns `Vector2i.zero()` when the engine has not been
initialized, the slot index is out of `[0, 3]`, or the slot has no live
pointer.pointerPos(0);
const const delta: Vector2idelta = const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.pointerDelta: (pointerIndex?: number) => Vector2iReturns the position delta `(pos - prevPos)` for a pointer slot since the previous frame.
Reflects movement accumulated between the previous and current frame.
Snapshotted and reset by the engine at `endFrame()`, which runs after
`update()` and `render()`. Returns `Vector2i.zero()` when the engine is
not initialized or `pointerIndex` is out of range.pointerDelta(0);
import uiui.kv('Pos', `${const pos: Vector2ipos.Vector2i.x: numberHorizontal component (defaults to 0).x},${const pos: Vector2ipos.Vector2i.y: numberVertical component (defaults to 0).y}`);
import uiui.kv('Delta', `${const delta: Vector2idelta.Vector2i.x: numberHorizontal component (defaults to 0).x},${const delta: Vector2idelta.Vector2i.y: numberVertical component (defaults to 0).y}`);
} else {
import uiui.kv('Pos', '--,--');
import uiui.kv('Delta', '--,--');
}
// toFixed(1) turns the number into text with one digit after the decimal point,
// so the readout does not jitter through long fractions.
import uiui.kv('Wheel', const scroll: numberscroll.toFixed(1));
// The meter fills up as you scroll down and empties as you scroll up. Dividing by
// the display height turns 0..240 pixels into the 0..1 fraction the meter
// expects.
import uiui.meter('Scroll pos', this.Demo.scrollPos: numberscrollPos / const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.displaySize: Vector2iActive logical render resolution in pixels.
This is the game/simulation coordinate space configured by the demo, not
the canvas element's CSS size. Each read returns a clone.displaySize.Vector2i.y: numberVertical component (defaults to 0).y, { width: numberwidth: const READOUTS_W: 152READOUTS_W - 20 });
import uiui.end();
}
// Buttons panel in the top-right: four read-only pips, one per pointer button, and
// a count of the touch slots in use. A pip lights up while its button is held and
// goes hollow when released. BT.isDown() checks held state, which is safe from
// render().
Demo.renderButtonPips(): voidrenderButtonPips() {
import uiui.begin(import UI_ANCHORSUI_ANCHORS.TOP_RIGHT, { y: numbery: const PANEL_Y: 26PANEL_Y, width: numberwidth: const BUTTONS_W: 152BUTTONS_W });
import uiui.panel('Buttons');
// Each pip pairs a label with the live held state of one button code.
import uiui.pip('A (left)', const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.isDown: (button: number, player?: number) => booleanChecks whether a button is currently held.
For pointer buttons (`BTN_POINTER_A..D`), the second parameter is the
pointer slot index (0 = mouse, 1-3 = touch / pen). For mouse slot 0:
`A` is left, `B` is right, `C` is middle, `D` is back / forward
(matches RetroBlit canonical, not DOM `PointerEvent.button` index).
Touch / pen slots only support `A`; B/C/D return `false`.
`button` accepts one or more bit flags from the `BTN_*` set (for example
`BT.BTN_A | BT.BTN_B`). Matching uses ANY semantics: returns `true` when
any selected button is held.
For face buttons (`BTN_UP`…`BTN_SELECT`), players `0` and `1` merge keyboard
and gamepad input (logical OR). Players `2` and `3` use gamepad only.
Pointer flags (`BTN_POINTER_*`) use the `player` argument as pointer slot.
`BTN_L2`, `BTN_R2` and `BTN_TRIGGER` are gamepad only (no keyboard mapping).isDown(const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.type BTN_POINTER_A: numberPrimary pointer button code.
Maps to mouse left for slot 0; touch contact for slots 1-3.BTN_POINTER_A, 0));
import uiui.pip('B (right)', const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.isDown: (button: number, player?: number) => booleanChecks whether a button is currently held.
For pointer buttons (`BTN_POINTER_A..D`), the second parameter is the
pointer slot index (0 = mouse, 1-3 = touch / pen). For mouse slot 0:
`A` is left, `B` is right, `C` is middle, `D` is back / forward
(matches RetroBlit canonical, not DOM `PointerEvent.button` index).
Touch / pen slots only support `A`; B/C/D return `false`.
`button` accepts one or more bit flags from the `BTN_*` set (for example
`BT.BTN_A | BT.BTN_B`). Matching uses ANY semantics: returns `true` when
any selected button is held.
For face buttons (`BTN_UP`…`BTN_SELECT`), players `0` and `1` merge keyboard
and gamepad input (logical OR). Players `2` and `3` use gamepad only.
Pointer flags (`BTN_POINTER_*`) use the `player` argument as pointer slot.
`BTN_L2`, `BTN_R2` and `BTN_TRIGGER` are gamepad only (no keyboard mapping).isDown(const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.type BTN_POINTER_B: numberSecondary pointer button code.
Maps to mouse right for slot 0 (matches RetroBlit canonical, not the
DOM `PointerEvent.button` index where 1 is middle and 2 is right).
Always `false` for touch slots 1-3.BTN_POINTER_B, 0));
import uiui.pip('C (middle)', const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.isDown: (button: number, player?: number) => booleanChecks whether a button is currently held.
For pointer buttons (`BTN_POINTER_A..D`), the second parameter is the
pointer slot index (0 = mouse, 1-3 = touch / pen). For mouse slot 0:
`A` is left, `B` is right, `C` is middle, `D` is back / forward
(matches RetroBlit canonical, not DOM `PointerEvent.button` index).
Touch / pen slots only support `A`; B/C/D return `false`.
`button` accepts one or more bit flags from the `BTN_*` set (for example
`BT.BTN_A | BT.BTN_B`). Matching uses ANY semantics: returns `true` when
any selected button is held.
For face buttons (`BTN_UP`…`BTN_SELECT`), players `0` and `1` merge keyboard
and gamepad input (logical OR). Players `2` and `3` use gamepad only.
Pointer flags (`BTN_POINTER_*`) use the `player` argument as pointer slot.
`BTN_L2`, `BTN_R2` and `BTN_TRIGGER` are gamepad only (no keyboard mapping).isDown(const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.type BTN_POINTER_C: numberTertiary pointer button code.
Maps to mouse middle for slot 0 (matches RetroBlit canonical, not the
DOM `PointerEvent.button` index where 1 is middle and 2 is right).
Always `false` for touch slots 1-3.BTN_POINTER_C, 0));
import uiui.pip('D (extra)', const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.isDown: (button: number, player?: number) => booleanChecks whether a button is currently held.
For pointer buttons (`BTN_POINTER_A..D`), the second parameter is the
pointer slot index (0 = mouse, 1-3 = touch / pen). For mouse slot 0:
`A` is left, `B` is right, `C` is middle, `D` is back / forward
(matches RetroBlit canonical, not DOM `PointerEvent.button` index).
Touch / pen slots only support `A`; B/C/D return `false`.
`button` accepts one or more bit flags from the `BTN_*` set (for example
`BT.BTN_A | BT.BTN_B`). Matching uses ANY semantics: returns `true` when
any selected button is held.
For face buttons (`BTN_UP`…`BTN_SELECT`), players `0` and `1` merge keyboard
and gamepad input (logical OR). Players `2` and `3` use gamepad only.
Pointer flags (`BTN_POINTER_*`) use the `player` argument as pointer slot.
`BTN_L2`, `BTN_R2` and `BTN_TRIGGER` are gamepad only (no keyboard mapping).isDown(const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.type BTN_POINTER_D: numberAuxiliary pointer button code.
Maps to mouse back/forward extra buttons (DOM `PointerEvent.button`
3 or 4) for slot 0. Always `false` for touch slots 1-3.BTN_POINTER_D, 0));
// How many fingers are down right now: slots 1-3 are the touch slots. The
// 13-pixel gap above it makes this panel's rows add up to the same 63 pixels as
// the readouts panel beside it (4 pips x 10 + 13 + 10).
let let touches: numbertouches = 0;
for (let let slot: numberslot = 1; let slot: numberslot < 4; let slot: numberslot++) {
if (const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.isPointerActive: (pointerIndex?: number) => booleanReports whether the given pointer slot has a live pointer.
For slot 0 (mouse) this is true while the mouse is hovering inside the
canvas; cleared on `pointerleave`. For slots 1-3 (touch / pen) this is
true while the contact is down.isPointerActive(let slot: numberslot)) {
let touches: numbertouches++;
}
}
import uiui.spacer(13);
import uiui.kv('Touches', let touches: numbertouches);
import uiui.end();
}
// A dim one-line note shown only after a touchscreen has been used. Fingers can move
// the crosshair, but there is no touch equivalent of the scroll wheel or the extra
// mouse buttons - better to say so than to let touch users hunt for something that
// cannot happen.
Demo.renderTouchNote(): voidrenderTouchNote() {
if (!import uiui.hasTouch()) {
return;
}
// A borderless group (no ui.panel call) is just floating text.
import uiui.begin(import UI_ANCHORSUI_ANCHORS.BOTTOM_RIGHT);
import uiui.label('Scroll wheel and right-click: desktop only', { color: stringcolor: 'dim' });
import uiui.end();
}
// A line through the recent pointer positions, all drawn in C_TRAIL.
Demo.renderTrail(): voidrenderTrail() {
if (this.Demo.activePointerSlot(): numberactivePointerSlot() < 0) {
return;
}
for (let let i: numberi = 1; let i: numberi < this.Demo.trail: {}trail.length; let i: numberi++) {
const [const ax: anyax, const ay: anyay] = this.Demo.trail: {}trail[let i: numberi - 1];
const [const bx: anybx, const by: anyby] = this.Demo.trail: {}trail[let i: numberi];
this.Demo.lineFrom: Vector2ilineFrom.Vector2i.set(x: number, y: number): Vector2iSets both components of this vector.
Modifies this vector directly for maximum performance.
WARNING: Mutates this vector. Don't use on frozen/cached singletons.set(const ax: anyax, const ay: anyay);
this.Demo.lineTo: Vector2ilineTo.Vector2i.set(x: number, y: number): Vector2iSets both components of this vector.
Modifies this vector directly for maximum performance.
WARNING: Mutates this vector. Don't use on frozen/cached singletons.set(const bx: anybx, const by: anyby);
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.drawLine: (p0: Vector2i, p1: Vector2i, paletteIndex: number) => voidDraws a pixel-perfect line between two points.
Uses rasterized line drawing without antialiasing.drawLine(this.Demo.lineFrom: Vector2ilineFrom, this.Demo.lineTo: Vector2ilineTo, const C_TRAIL: 2C_TRAIL);
}
}
// Crosshair drawn at the current pointer position. Only shown while a mouse (slot 0)
// or touch (slots 1-3) pointer is over the canvas.
Demo.renderCrosshair(): voidrenderCrosshair() {
const const slot: numberslot = this.Demo.activePointerSlot(): numberactivePointerSlot();
if (const slot: numberslot < 0) {
return;
}
const const pos: Vector2ipos = const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.pointerPos: (pointerIndex?: number) => Vector2iReturns the position of the pointer in the given slot, in display coordinates.
Slot 0 is the mouse; slots 1 through 3 are touch / pen contacts assigned
in arrival order. Returns `Vector2i.zero()` when the engine has not been
initialized, the slot index is out of `[0, 3]`, or the slot has no live
pointer.pointerPos(const slot: numberslot);
// Two 1-pixel-thick arms that cross at the pointer, each 13 pixels long (6 on
// either side plus the center pixel). They are drawn with ui.drawRectFill(), the
// sprite version of BT.drawRectFill(): the engine draws every BT.drawLine() and
// BT.drawRectFill() before any sprite, and the kit panels are sprites, so a plain
// line would vanish under a panel whenever the pointer moved over one.
const const span: numberspan = const CROSSHAIR_ARM: 6CROSSHAIR_ARM * 2 + 1;
this.Demo.armRect: Rect2iarmRect.Rect2i.set(x: number, y: number, width: number, height: number): Rect2iSets all components of this rectangle.
Modifies this rectangle directly for maximum performance.
WARNING: Mutates this rectangle. Don't use on frozen/cached singletons.set(const pos: Vector2ipos.Vector2i.x: numberHorizontal component (defaults to 0).x - const CROSSHAIR_ARM: 6CROSSHAIR_ARM, const pos: Vector2ipos.Vector2i.y: numberVertical component (defaults to 0).y, const span: numberspan, 1);
import uiui.drawRectFill(this.Demo.armRect: Rect2iarmRect, const C_CROSSHAIR: 1C_CROSSHAIR);
this.Demo.armRect: Rect2iarmRect.Rect2i.set(x: number, y: number, width: number, height: number): Rect2iSets all components of this rectangle.
Modifies this rectangle directly for maximum performance.
WARNING: Mutates this rectangle. Don't use on frozen/cached singletons.set(const pos: Vector2ipos.Vector2i.x: numberHorizontal component (defaults to 0).x, const pos: Vector2ipos.Vector2i.y: numberVertical component (defaults to 0).y - const CROSSHAIR_ARM: 6CROSSHAIR_ARM, 1, const span: numberspan);
import uiui.drawRectFill(this.Demo.armRect: Rect2iarmRect, const C_CROSSHAIR: 1C_CROSSHAIR);
}
// "Move pointer over canvas" hint, centered in the open space under the panels.
// Only shown while no mouse or touch pointer is over the canvas, so newcomers know
// what to do. Uses the theme's dim text color so it matches the rest of the UI.
Demo.renderPointerHint(): voidrenderPointerHint() {
if (this.Demo.activePointerSlot(): numberactivePointerSlot() >= 0) {
return;
}
// The open space runs from the bottom of the panels to the bottom of the screen.
// Every character of the system font is FONT_W (6) pixels wide, so starting the
// text half its width left of the middle centers it; a caption's capital letters
// cover 7 rows starting 1 row above the y it is given, so centerY - 2 centers
// them vertically.
const const text: "Move pointer over canvas"text = 'Move pointer over canvas';
const const screen: Vector2iscreen = const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.displaySize: Vector2iActive logical render resolution in pixels.
This is the game/simulation coordinate space configured by the demo, not
the canvas element's CSS size. Each read returns a clone.displaySize;
const const centerX: anycenterX = Math.floor(const screen: Vector2iscreen.Vector2i.x: numberHorizontal component (defaults to 0).x / 2);
const const centerY: anycenterY = Math.floor((const PANELS_BOTTOM: numberPANELS_BOTTOM + const screen: Vector2iscreen.Vector2i.y: numberVertical component (defaults to 0).y) / 2);
import uiui.caption(const centerX: anycenterX - (const text: "Move pointer over canvas"text.length * import FONT_WFONT_W) / 2, const centerY: anycenterY - 2, const text: "Move pointer over canvas"text, { color: stringcolor: 'dim' });
}
// First active pointer slot this frame: mouse (0) preferred, then touch (1-3).
// Returns -1 when no pointer is over the canvas.
Demo.activePointerSlot(): numberactivePointerSlot() {
if (const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.isPointerActive: (pointerIndex?: number) => booleanReports whether the given pointer slot has a live pointer.
For slot 0 (mouse) this is true while the mouse is hovering inside the
canvas; cleared on `pointerleave`. For slots 1-3 (touch / pen) this is
true while the contact is down.isPointerActive(0)) {
return 0;
}
for (let let slot: numberslot = 1; let slot: numberslot < 4; let slot: numberslot++) {
if (const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.isPointerActive: (pointerIndex?: number) => booleanReports whether the given pointer slot has a live pointer.
For slot 0 (mouse) this is true while the mouse is hovering inside the
canvas; cleared on `pointerleave`. For slots 1-3 (touch / pen) this is
true while the contact is down.isPointerActive(let slot: numberslot)) {
return let slot: numberslot;
}
}
return -1;
}
}
function bootstrap(DemoClass: DemoConstructor, options?: BootstrapOptions): Promise<boolean>One-liner bootstrap function for BLIT386 demos.
Handles canvas retrieval and engine initialization. Backend selection
(WebGPU or software fallback) is managed internally by BTAPI.
This function provides a streamlined way to start a demo with sensible defaults
while allowing customization through options.bootstrap(class DemoDemo);