SDL2 for the POSIX build
Windows, drawing, keyboard, mouse, images, text and sound for games,
through the shared libraries of SDL2. Nothing is linked into
lugforth: the libraries are opened at run time
(dlopen) the first time you use them.
sdl2 \ load, sdl2 becomes your vocabulary
3 to zoom 320 240 screen \ 320x240 logical pixels, shown 3 times bigger
: frame events black color cls red color 10 10 50 50 box flip ;
: run begin frame quit? until close-screen ;
run
Examples: sdl2_demo.fs (tour of the words),
sdl2_breakout.fs (a game, about 200 lines),
sdl2_drums.fs (16-step drum machine),
fm/sdl2_fm.fs (two operator FM synthesizer, MIDI in, kept
out of the interpreter: see fm/README.md),
sdl2_pianoroll.fs (piano roll with soundfont playback and
export to .mid and ABC, POSIX only),
sdl2_roguelike.fs (tile sheet, with music),
sdl2_3d.fs (turning wireframe cube, pyramid and
octahedron), sdl2_2048.fs (2048 with a slide and pop
animation, --no-anim turns it off; digits drawn from a 3x5
bitmap), pinball/pinball.fs (a pinball in the style of
Night Mission, fixed point physics). Breakout, drums, the FM synthesizer
and roguelike also run in the browser.
All words: SDL2-words.md. The short words also exist in the web build, see SDL2-web.md.
Packages
| Loader | Library needed | Debian/Ubuntu package | Adds |
|---|---|---|---|
sdl2 |
libSDL2-2.0.so.0 | libsdl2-2.0-0 |
window, drawing, input, beeps |
sdl2-image |
libSDL2_image-2.0.so.0 | libsdl2-image-2.0-0 |
sprites (PNG, JPG, BMP...) |
sdl2-ttf |
libSDL2_ttf-2.0.so.0 | libsdl2-ttf-2.0-0 |
text with TrueType fonts |
sdl2-mixer |
libSDL2_mixer-2.0.so.0 | libsdl2-mixer-2.0-0 |
sound files, music, mixing, soundfont |
sdl2-abc |
(sdl2-mixer, and a soundfont for MIDI) |
fluid-soundfont-gm or your own .sf2 |
tunes in ABC notation |
Typing a loader again puts sdl2 back on top. All words
below live in the sdl2 vocabulary, with the raw C names
(SDL_Init, ...) for anything without a short word.
sdl2 is also where your definitions go.
Short words (sdl2)
Screen: screen ( w h -- ), close-screen,
zoom (value, set before screen),
title ( a n -- ), fullscreen ( f -- ),
cursor ( f -- ), width,
height.
Colors: color ( $rrggbb -- ),
rgb ( r g b -- ), rgba ( r g b a -- ),
alpha ( a -- ), and named colors
black white red green blue yellow cyan magenta orange purple pink brown gray
(red color), and ink ( n -- ), a color by
number from 0 to 15 (0 black, 1 red, 2 green, 3 yellow, 4 blue, 5
magenta, 6 cyan, 7 white...).
Drawing: cls, dot ( x y -- ),
line ( x1 y1 x2 y2 -- ), box ( x y w h -- ),
frame ( x y w h -- ), hline ( x y w -- ),
disc ( cx cy r -- ), circle ( cx cy r -- ),
flip, pixel@ ( x y -- $rrggbb ).
Frame loop: events (call once per frame),
quit?, quit!, wait,
ticks ( -- ms ), delay ( ms -- ),
dt (ms taken by the last frame).
Input: pressed? ( key -- f ) with [char] a,
key-left key-right key-up key-down key-space key-enter key-esc key-tab key-backspace,
LEFT-BUTTON MIDDLE-BUTTON RIGHT-BUTTON,
mouse-x, mouse-y, last-key,
wheel. Same names and meaning as the generic graphics
interface. scan? ( scancode -- f ) tells whether a key is
held by its place on the keyboard (SDL scancode: 4 is the key A of a
QWERTY, 20 is Q...), whatever the layout: use it for a piano or WASD
style row of keys that must not move on an AZERTY keyboard.
Beeps (no library beyond libSDL2): beep ( freq ms -- )
queues a tone, so several beep in a row play as a melody.
wave sets the shape (0 square, 1 triangle, 2 saw, 3 noise,
4 sine), as color sets the color: 1 wave, or
the words square triangle saw noise sine.
beep-volume ( n -- ) and pitch-drop ( n -- )
work the same way; read a setting back with wave@,
beep-volume@, pitch-drop@.
to wave still works. Also silence,
beeping?.
play ( a n -- ) queues notes written in MML (Music Macro
Language, the PLAY of the BASICs of MSX, GW-BASIC and
QBasic): s" t140 o4 l8 c d e c e4 g4 r4 >c2" play.
| Command | Meaning |
|---|---|
c d e f g a b |
a note; # or + after it sharpens,
- flattens |
| a number after a note | its length: 4 quarter, 8 eighth, 1 whole; a dot adds half
(c4.) |
r or p |
a rest (same length syntax) |
o4 |
the octave, 0 to 8; o4 c is the middle C (262 Hz) |
< > |
one octave down, up |
l8 |
the default length |
t120 |
the tempo, in quarters a minute |
n49 |
a note by number: n1 is the lowest C (octave 0), n49 is the middle C; n0 is a rest |
& |
ties a note to the next one of the same pitch:
c4&c8 |
v8 |
the volume, 0 to 15 (it sets beep-volume) |
w1 |
the wave, as 1 to wave (not part of the usual MML) |
Spaces are free; the settings stay from one play to the
next. Differences from the dialects: the octave numbering is the MSX one
(o4 c is the middle C; check a tune written for another
dialect, they differ); no chords, no m modes (legato,
staccato) and no x (play a string variable). It works at
the prompt after sdl2 (the console window, the boards, the
web page).
Several voices: a comma in the string starts another voice, and the
voices start together
(s" o4 e d c d e e e2, o3 c1 g1" play). 4 voices at most,
512 notes each; each voice begins with the octave, length, tempo, wave
and volume the play started with. On Linux the voices are
mixed into one buffer and queued whole (so a very long tune takes a
moment to prepare); on the boards each voice has its own queue; on the
web page the notes of every voice are scheduled from the same
moment.
Envelope: adsr ( attack decay sustain release -- ) (ms,
ms, percent, ms) shapes every beep and every note of
play; the release is the end of the note, not added to it.
adsr@ reads the values, adsr-off removes the
envelope (the default). Hits and ABC tunes are not affected.
Hits: drum-hit ( freq ms -- ) starts at once and plays
over other sounds (8 at a time), fading out; pitch-drop
(percent) makes the pitch fall, enough for a kick drum. Call
flip or delay regularly, as a game loop does.
Use beep or drum-hit in a program, not
both.
Images (sdl2-image)
load-image ( a n -- img ), free-image,
image-size ( img -- w h ),
draw ( img x y -- ),
draw-size ( img x y w h -- ),
draw-part ( img sx sy sw sh x y w h -- ) for sprite sheets,
image-alpha, image-tint. Load images after
screen. A name with directories that is not found is tried
again without them, so s" examples/tiles.png" load-image
also works from inside examples/.
Text (sdl2-ttf)
load-font ( a n pt -- font ),
font ( font -- ), free-font,
text ( x y a n -- ) in the current color,
text-size ( a n -- w h ).
Sound (sdl2-mixer)
load-sound ( a n -- snd ), play ( snd -- ),
free-sound, sound-volume,
stop-sounds, sounds-playing,
load-music ( a n -- mus ), music ( mus -- )
(loops), music-once, stop-music,
music-volume, music?, free-music.
Overlapping sounds are mixed.
Music files: OGG, MP3, FLAC, Opus, WAV, MOD and MIDI, as far as your
SDL2_mixer has the decoders. MIDI needs a soundfont:
soundfont ( a n -- ) sets a .sf2 or
.sf3 (soundfont? returns a flag instead of
throwing). The name is tried as given, then in the current directory,
then in the script's directory, so s" mygame.sf2" soundfont
finds a file beside the program. Without one, FluidSynth uses the system
soundfont if there is one.
Tunes (sdl2-abc)
Tunes written in ABC notation,
a text format for melodies, can be played with the soundfont. Type
sdl2-abc once (it loads sdl2-mixer if needed).
Since ABC uses the bar character, write the tune with
r~ ... ~, a multi-line string that ends at a tilde:
sdl2-abc
s" gm.sf2" soundfont? drop \ optional, a soundfont beside the program
: tune r~ X:1
T:Scale
M:4/4
L:1/4
Q:1/4=120
K:C
%%MIDI program 0
C D E F | [CEG]2 z2 |
~ ;
tune abc-play \ once; abc-loop repeats; tune-stop stops
abc ( a n -- ) reads the tune; tune-play,
tune-loop, tune-stop and
tune-playing? play it; abc-play and
abc-loop do both. After abc you can change
tune-bpm and tune-program (General MIDI
instrument), read tune-ms and tune-notes, or
write a MIDI file with tune-save.
Understood: K: L: M:
Q: and %%MIDI program n; notes, accidentals,
octave marks, lengths, rests, chords, ties, broken rhythm, tuplets, bar
lines. Skipped: chord symbols, decorations, grace notes, comments,
lyrics. Not supported: repeats, several voices, tempo changes. At most
1024 notes. The web build has the same words with a simple
synthesizer.
Console window
(sdl2_console.fs)
In the console window, graphic makes the drawing words
work at the prompt (0 0 100 100 line): a 640x480 texture
receives them, drawn under the text, centered, in proportion;
cls clears it, flip redraws the window.
w h graphic-size sets the size of the texture (16 to 4096),
kept in proportion in the window. A program that calls
screen ends it. The pen is saved and restored around each
redraw.
lugforth --gui [file.fs] opens a console window
(type and key go to an SDL window). The code
is part of the binary: gui-load loads it, then
desktop console starts it (Android uses
gui-load smooth-font console). Without SDL2 or a display,
--gui says so and keeps the terminal.
The menu is hidden: the button at the top right, or F10, shows it (a
right click on the open menu closes it; a right click on the button
hides the button, and on its place shows it again) (Examples, only when
the folder holds .fs files, View, Colors, Font, Help).
Examples lists the .fs files of
LUGFORTH_EXAMPLES (default: the current directory). Colors
has a color picker for the text and for the background (a square of
saturation and brightness, a strip of hues, the hex value) and a few
themes. Alt+Enter, or View, toggles full screen. Font lists the fixed
width TrueType and OpenType fonts found in
/usr/share/fonts, ~/.local/share/fonts and
~/.fonts (the wheel scrolls a long menu). View also has
More margin and Less margin (margin= in the
.ini, in pixels). Also: mouse selection (copied), middle
click or Ctrl+V to paste, wheel or PageUp/PageDown to scroll back,
Ctrl+wheel, Ctrl +/-, Ctrl 0 for the text size. ASCII only.
Settings are kept in lugforth.ini in
~/.local/share/lugforth (or
LUGFORTH_HOME):
size=15
fg=D2D2D2
bg=000000
font=/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf
margin=12
fullscreen=Alt+Enter
fullscreen= is the key of full screen:
Alt+Enter, F11,
Ctrl+Shift+F...
font= wins over LUGFORTH_FONT, then DejaVu
Sans Mono and other common fonts of the system.
ports/posix/sdl2_console_tests.fs tests the settings (dummy
drivers, like the other SDL tests).
Limits
- Calls pass integers and pointers only: no floats or structs by value
(
SDL_RenderCopyEx, rotation...). - No callbacks from C into Forth: audio uses queues
(
beep,drum-hit) or the mixer. - Mouse positions are in logical pixels, whatever the
zoom. pressed?reads the held state once perevents; a press shorter than one frame is missed.- Names ignore case, so the function
SDL_Quitis bound assdl-shutdown;SDL_QUITis the event constant.
Tests
They need the libraries, so ninja posix does not run
them:
SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy out/posix/lugforth ports/posix/sdl2_tests.fsSame for sdl2_media_tests.fs and
sdl2_abc_tests.fs.
SDL2 word reference
Every word of the sdl2 vocabulary, for the POSIX build
(out/posix/lugforth). For a tour with examples read SDL2.md first. This page is the complete list.
The words come from five loaders, each loading its source the first
time it is typed. They all add to the same sdl2
vocabulary:
| Loader | Library | Section |
|---|---|---|
sdl2 |
libSDL2-2.0.so.0 |
Core |
sdl2-image |
libSDL2_image-2.0.so.0 |
Images |
sdl2-ttf |
libSDL2_ttf-2.0.so.0 |
Text |
sdl2-mixer |
libSDL2_mixer-2.0.so.0 |
Sound |
sdl2-abc |
(loads sdl2-mixer) |
Tunes |
Stack notation: x y are pixel coordinates,
w h sizes, a n a Forth string (address,
length), z a zero terminated C string, f a
flag, $rrggbb a color. img snd mus font are
handles returned by the load- words. Values are read by
name and changed with to: 3 to zoom.
Raw C bindings keep the C name and argument order. Each one pushes
exactly one result. When the table says void the C function
returns nothing but a value is still pushed: drop it. Int
results that can be negative (-1 for an error) should go
through sign-extend from the posix
vocabulary.
Word lists: alphabetical index.
Core (sdl2)
Screen
| Word | Stack | What it does |
|---|---|---|
screen |
( w h -- ) |
Open a window of w*zoom by h*zoom pixels
whose drawing area is w by h logical pixels (scaled, aspect kept).
Closes a previous screen first. Uses an accelerated renderer with vsync,
or the software one if that fails. Throws with the SDL message on
failure. |
close-screen |
( -- ) |
Destroy the renderer and window, run close-hook, close
the audio device and call sdl-shutdown (the C function
SDL_Quit). |
screen-open? |
( -- f ) |
True while a screen is open. |
zoom |
( -- n ) |
Value, default 1. Window scale factor used by the next
screen: 3 to zoom. |
width |
( -- n ) |
Value. Logical width given to the last screen. |
height |
( -- n ) |
Value. Logical height given to the last screen. |
title |
( a n -- ) |
Set the window title. |
fullscreen |
( f -- ) |
Switch desktop fullscreen on (true) or off (false). |
cursor |
( f -- ) |
Show (true) or hide (false) the mouse cursor. |
window-handle |
( -- a ) |
Value. The SDL_Window*, 0 when no screen is open. |
renderer |
( -- a ) |
Value. The SDL_Renderer*, 0 when no screen is open.
Pass it to raw calls. |
sdl-error |
( -- a n ) |
Text of the last SDL error. |
Colors
| Word | Stack | What it does |
|---|---|---|
color |
( $rrggbb -- ) |
Set the drawing color, opaque. red color,
$102030 color. |
rgb |
( r g b -- ) |
Set the drawing color from components 0 to 255, opaque. |
rgba |
( r g b a -- ) |
Set the drawing color and alpha. Alpha below 255 blends with what is already drawn. |
alpha |
( a -- ) |
Change only the alpha of the current color. |
black white red
green blue yellow
cyan magenta orange
purple pink brown
gray |
( -- $rrggbb ) |
Named colors, constants:
$000000 $ffffff $ff0000 $00c000 $0000ff $ffff00 $00ffff $ff00ff $ff8000 $800080 $ff80c0 $804000 $808080.
Use as red color. |
pen-r pen-g pen-b
pen-a |
( -- n ) |
Values. Components of the current color. Read them, set the color
with rgba. |
ink |
( n -- ) |
Set the drawing color by number, as on an 8 bit micro: 0 black, 1
red, 2 green, 3 yellow, 4 blue, 5 magenta, 6 cyan, 7 white, 8 to 15
gray, orange, pink, brown, purple, light green, light blue, light gray.
1 ink is red color. Opaque. |
pen |
( -- n ) |
Value. Current color packed as
r + g<<8 + b<<16 + a<<24, the layout of
an SDL_Color passed by value (used by
text). |
Drawing
| Word | Stack | What it does |
|---|---|---|
cls |
( -- ) |
Fill the whole screen with the current color. |
dot |
( x y -- ) |
Draw one pixel. Also named plot (see Aliases). |
line |
( x1 y1 x2 y2 -- ) |
Draw a line, both ends included. |
box |
( x y w h -- ) |
Filled rectangle. |
frame |
( x y w h -- ) |
Rectangle outline. |
hline |
( x y w -- ) |
Horizontal line of width w starting at x y. |
disc |
( cx cy r -- ) |
Filled circle of radius r. |
circle |
( cx cy r -- ) |
Circle outline of radius r. |
flip |
( -- ) |
Show what was drawn since the last flip. Waits for
vsync when available, and updates dt. Also tops up the
sound of drum-hit. |
pixel@ |
( x y -- $rrggbb ) |
Read back one pixel of the screen. Useful for color based collisions. |
Timing
| Word | Stack | What it does |
|---|---|---|
ticks |
( -- ms ) |
Milliseconds since SDL started. |
delay |
( ms -- ) |
Sleep for ms milliseconds, topping up the sound of
drum-hit meanwhile. |
dt |
( -- ms ) |
Value. Milliseconds between the last two flip. |
Input
| Word | Stack | What it does |
|---|---|---|
events |
( -- ) |
Handle every pending event: keys, mouse, window close. Call once per frame, before reading input. |
wait |
( -- ) |
Block until one event arrives and handle it. |
quit? |
( -- f ) |
True once the window was closed or quit! was
called. |
quit! |
( -- ) |
Set the quit flag, as if the window was closed. |
pressed? |
( key -- f ) |
True while the key or mouse button is held, as of the last
events. Key is a character ([char] a, case
ignored), a key- constant, or a -BUTTON
constant. A press released within the same frame is not seen. |
scan? |
( scancode -- f ) |
True while the key at that place of the keyboard is held (SDL scancode: 4 to 29 are A to Z of a QWERTY, 30 to 39 are 1 to 9 and 0, 54 55 56 are comma, period, slash), whatever the layout. |
last-key |
( -- n ) |
Value. Code of the last key pressed. |
mouse-x mouse-y |
( -- n ) |
Values. Last mouse position seen in events, in logical pixels (the
screen size), whatever the zoom. |
wheel |
( -- n ) |
Value. Mouse wheel movement handled by the last events
(up is positive). |
key-left key-right key-up
key-down |
( -- n ) |
Constants. Arrow keys, for pressed?. |
key-space key-enter key-esc
key-tab key-backspace |
( -- n ) |
Constants. 32 13 27 9 8, for
pressed?. |
LEFT-BUTTON MIDDLE-BUTTON
RIGHT-BUTTON |
( -- n ) |
Constants 255 254 253, for pressed?. Same
values as the generic graphics interface. |
Beeps and hits
| Word | Stack | What it does |
|---|---|---|
beep |
( freq ms -- ) |
Queue a tone of freq Hz lasting ms milliseconds. Tones play one
after the other, so a series of beep is a melody. Silent if
no audio device could be opened. |
drum-hit |
( freq ms -- ) |
Play a percussive sound now, over whatever else is playing: it fades
out linearly and its pitch can fall (pitch-drop). Up to 8
at a time, a ninth cuts the oldest. Uses wave and
beep-volume. The mix is queued about 50 ms ahead by
drum-hit, flip and delay, so call
flip or delay regularly. A beep
waits for the queue while a hit does not, so do not expect them to line
up. |
pitch-drop |
( n -- ) |
Setting (pitch-drop@ reads it), default 0. Percent by
which the pitch of the next hits falls while they play, 0 to 100. Noise
has no pitch. |
silence |
( -- ) |
Drop all queued beeps and stop all hits. |
latency |
( -- n ) |
Value, default 2205. Samples kept queued by flip,
delay and drum-hit (50 ms); lower it for a
more reactive instrument. |
render-hook active-hook |
( -- xt ) |
Values, 0 by default. Extra sources for the mix of
pump: render-hook is run as
( n -- ) and adds n samples to the cell array
mix, active-hook as ( -- f )
tells that it has sound to give. sdl2-fm uses them. |
beeping? |
( -- f ) |
True while queued beeps or hits remain. |
wave |
( n -- ) |
Setting. Shape of the next beeps: 0 square, 1 triangle, 2 saw, 3
noise, 4 sine (wave@ reads it; to wave works
too). |
beep-volume |
( n -- ) |
Setting (beep-volume@ reads it), default 3000. Beep
amplitude, 0 to 32767. |
square triangle saw
noise sine |
( -- ) |
Choose the wave of the next beeps by name: 0 wave,
1 wave, 2 wave, 3 wave,
4 wave. |
play |
( a n -- ) |
Play a melody written as text (MML, as on the MSX), queued like
beep:
s" t140 o4 l8 c d e c e4 g4 r4 >c2" play. Notes
c d e f g a b, # or + sharp,
- flat, then a length (4 quarter, 8 eighth, a dot lengthens
by half); r or p a rest; o4
octave, < one octave down, > one up;
l8 default length; t120 tempo in quarters a
minute; w1 the wave; v8 the volume 0 to 15;
n49 a note by number (n0 a rest, 49 is the
middle C); c4&c8 ties two notes of the same pitch.
Spaces are free; the settings stay from one play to the
next. A comma starts another voice, played together with the others (4
voices, 512 notes each, each begins with the octave, length, tempo, wave
and volume the play started with). Only in the short words
(sdl2), not in the mixer: with sdl2-mixer,
play is the word that plays a sound. |
adsr |
( a d s r -- ) |
Envelope of the beeps and of play: attack and decay in
ms, sustain in percent of the peak, release in ms (the end of the note,
not added to it). Hits and ABC tunes are not affected. Off by
default. |
adsr@ |
( -- a d s r ) |
The four values of adsr. |
adsr-off |
( -- ) |
Back to the plain note, without envelope. |
setting |
( n "name" -- ) |
Defines a setting, as wave, beep-volume
and pitch-drop are: 3000 beep-volume stores,
to beep-volume too, and beep-volume@ reads
it. |
audio-open |
( -- ) |
Open the beep audio device now instead of at the first
beep. |
audio-open? |
( -- f ) |
True when the beep audio device is open. |
sample-rate |
( -- n ) |
Constant 44100. Beep sample rate. |
sdl |
( z n "name" -- ) |
Handle of libSDL2 made by shared-library.
z" SDL_Init" 1 sdl SDL_Init defines a word calling that C
function with n arguments. |
Constants (core)
| Constant | Value |
|---|---|
SDL_INIT_AUDIO |
$00000010 |
SDL_INIT_VIDEO |
$00000020 |
SDL_INIT_EVENTS |
$00004000 |
SDL_WINDOW_SHOWN |
$00000004 |
SDL_WINDOW_RESIZABLE |
$00000020 |
SDL_WINDOW_FULLSCREEN_DESKTOP |
$00001001 |
SDL_WINDOWPOS_CENTERED |
$2FFF0000 |
SDL_RENDERER_SOFTWARE |
$00000001 |
SDL_RENDERER_ACCELERATED |
$00000002 |
SDL_RENDERER_PRESENTVSYNC |
$00000004 |
SDL_BLENDMODE_NONE |
0 |
SDL_BLENDMODE_BLEND |
1 |
SDL_BLENDMODE_ADD |
2 |
SDL_PIXELFORMAT_ARGB8888 |
$16362004 |
SDL_QUIT |
$0100 |
SDL_KEYDOWN |
$0300 |
SDL_KEYUP |
$0301 |
SDL_MOUSEMOTION |
$0400 |
SDL_MOUSEBUTTONDOWN |
$0401 |
SDL_MOUSEBUTTONUP |
$0402 |
SDL_MOUSEWHEEL |
$0403 |
AUDIO_S16SYS |
$8010 |
#voices |
8 |
Raw C bindings (core)
sdl-shutdown is the C function SDL_Quit:
lugforth ignores case, so it could not share its name with the
SDL_QUIT event constant.
| Word | Stack | Args |
|---|---|---|
SDL_Init |
( n -- n ) |
1 |
SDL_InitSubSystem |
( n -- n ) |
1 |
sdl-shutdown |
( -- void ) |
0 |
SDL_GetError |
( -- z ) |
0 |
SDL_SetHint |
( z z -- n ) |
2 |
SDL_GetTicks |
( -- n ) |
0 |
SDL_Delay |
( n -- void ) |
1 |
SDL_CreateWindow |
( z x y w h flags -- a ) |
6 |
SDL_DestroyWindow |
( a -- void ) |
1 |
SDL_SetWindowTitle |
( a z -- void ) |
2 |
SDL_SetWindowFullscreen |
( a n -- n ) |
2 |
SDL_ShowCursor |
( n -- n ) |
1 |
SDL_CreateRenderer |
( a n n -- a ) |
3 |
SDL_DestroyRenderer |
( a -- void ) |
1 |
SDL_RenderSetLogicalSize |
( a n n -- n ) |
3 |
SDL_SetRenderDrawColor |
( a r g b a -- n ) |
5 |
SDL_SetRenderDrawBlendMode |
( a n -- n ) |
2 |
SDL_RenderClear |
( a -- n ) |
1 |
SDL_RenderPresent |
( a -- void ) |
1 |
SDL_RenderDrawPoint |
( a x y -- n ) |
3 |
SDL_RenderDrawLine |
( a x y x y -- n ) |
5 |
SDL_RenderDrawRect |
( a a -- n ) |
2 |
SDL_RenderFillRect |
( a a -- n ) |
2 |
SDL_RenderCopy |
( a a a a -- n ) |
4 |
SDL_RWFromFile |
( z z -- a ) |
2 |
SDL_RenderReadPixels |
( a a n a n -- n ) |
5 |
SDL_CreateTexture |
( a n n n n -- a ) |
5 |
SDL_CreateTextureFromSurface |
( a a -- a ) |
2 |
SDL_UpdateTexture |
( a a a n -- n ) |
4 |
SDL_QueryTexture |
( a a a a a -- n ) |
5 |
SDL_SetTextureColorMod |
( a r g b -- n ) |
4 |
SDL_SetTextureAlphaMod |
( a n -- n ) |
2 |
SDL_SetTextureBlendMode |
( a n -- n ) |
2 |
SDL_DestroyTexture |
( a -- void ) |
1 |
SDL_FreeSurface |
( a -- void ) |
1 |
SDL_PollEvent |
( a -- n ) |
1 |
SDL_WaitEvent |
( a -- n ) |
1 |
SDL_PushEvent |
( a -- n ) |
1 |
SDL_GetMouseState |
( a a -- n ) |
2 |
SDL_GetKeyboardState |
( a -- a ) |
1 |
SDL_OpenAudioDevice |
( z n a a n -- n ) |
5 |
SDL_CloseAudioDevice |
( n -- void ) |
1 |
SDL_PauseAudioDevice |
( n n -- void ) |
2 |
SDL_QueueAudio |
( n a n -- n ) |
3 |
SDL_GetQueuedAudioSize |
( n -- n ) |
1 |
SDL_ClearQueuedAudio |
( n -- void ) |
1 |
Internal words (core)
Used by the words above. Listed because they are in the vocabulary and can hide or be hidden by your own names.
| Word | Stack | What it does |
|---|---|---|
8bit |
( n -- n ) |
Keep the low 8 bits. |
?sdl |
( n -- ) |
Throw with the SDL error text when an int result is negative. |
ev |
( -- a ) |
Buffer holding the current SDL_Event, 64 bytes. |
ev-type |
( -- n ) |
Type of the event in ev. |
rect |
( -- a ) |
Shared SDL_Rect buffer (four int32: x y w h). |
rect-at! |
( x y w h a -- ) |
Fill an SDL_Rect at address a. |
rect! |
( x y w h -- ) |
Fill rect. |
px |
( -- a ) |
4 byte buffer used by pixel@. |
span-x |
( -- n ) |
Value. Running half width used by disc and
circle. |
span-step |
( i r -- ) |
Shrink span-x until it fits the circle of radius r at
row i. |
dots4 |
( cx cy a b -- ) |
Plot the four mirrored points cx+-a, cy+-b. |
key-index |
( sym scancode -- n ) |
Turn an SDL key into the 0 to 255 code used by
pressed?: ASCII as is, special keys as 128 plus
scancode. |
key-event |
( f -- ) |
Store a key press (1) or release (0) from ev. |
button-event |
( f -- ) |
Store a mouse button press or release from ev. |
handle-event |
( -- ) |
Dispatch the event in ev on its type. |
keys |
( -- a ) |
256 byte table of held keys and buttons. |
fold-case |
( k -- k ) |
Lowercase an upper case letter code. |
quitting |
( -- n ) |
Value. Quit flag behind quit?. |
close-hook |
( -- xt ) |
Value. Optional xt run by close-screen; the mixer uses
it. |
last-flip |
( -- ms ) |
Value. Time of the last flip. |
make-renderer |
( -- a ) |
Create the renderer, accelerated with vsync or else software. |
audio-dev |
( -- n ) |
Value. Beep audio device id, 0 when closed. |
audio-spec |
( -- a ) |
SDL_AudioSpec buffer, 32 bytes. |
seed |
( -- n ) |
Value. Noise generator state. |
noise-sample |
( -- n ) |
Next noise sample. |
wave-sample |
( phase -- n ) |
Sample of the current wave at phase 0 to 65535. |
fill-beep |
( freq n buf -- ) |
Write n samples of a tone into buf, with a short fade out. |
base-name |
( a n -- a' n' ) |
A file name without its directories. |
audio-hook |
( -- xt ) |
Value. Optional xt run by flip and delay;
it is set to pump. |
run-audio-hook |
( -- ) |
Run audio-hook if set. |
#voices |
( -- 8 ) |
Constant. Number of hits that can play at once. |
voice-size |
( -- n ) |
Constant. Bytes of one voice: 7 cells (phase, frequency, samples left, samples in all, wave, pitch drop, volume). |
voices |
( -- a ) |
Table of the voices. |
voice |
( i -- a ) |
Address of voice i. |
clear-voices |
( -- ) |
Stop all hits without touching the audio queue. |
left@ |
( i -- n ) |
Samples left to play in voice i, 0 when idle. |
voices-active |
( -- n ) |
Number of hits playing. |
voice-free |
( -- i ) |
An idle voice, or else the one closest to its end. |
start-voice |
( freq ms -- ) |
Start a hit in a voice without mixing it yet (drum-hit
is this plus pump). |
pitch-at |
( freq drop elapsed total -- hz ) |
Frequency part way through a hit whose pitch falls by drop percent. |
mix |
( -- a ) |
4096 cells where the voices are added up. |
mixed |
( -- a ) |
4096 samples of 16 bits, the mix ready to queue. |
latency |
( -- n ) |
Constant 2205. Samples kept queued ahead, 50 ms. |
mix-voice |
( v n -- ) |
Add up to n samples of voice v into mix. |
render |
( n buf -- ) |
Mix the voices into n 16 bit samples of buf, clipped. |
pump |
( -- ) |
Top the audio queue up to latency samples with the mix
of the voices, unless none is playing. |
Images (sdl2-image)
Type sdl2-image once, then load images after
screen.
| Word | Stack | What it does |
|---|---|---|
load-image |
( a n -- img ) |
Load a PNG, JPG, BMP... file into a texture. A name with directories that is not found is tried again without them, so a program finds its files beside it. Throws with the SDL message on failure. Open the screen first. |
free-image |
( img -- ) |
Destroy the texture. |
image-size |
( img -- w h ) |
Size of the image in pixels. |
draw |
( img x y -- ) |
Draw the image at its natural size, top left at x y. |
draw-size |
( img x y w h -- ) |
Draw the image stretched to w by h. |
draw-part |
( img sx sy sw sh x y w h -- ) |
Draw the part sx sy sw sh of the image into x y w h. For sprite sheets and tiles. |
image-alpha |
( img a -- ) |
Set the image opacity, 0 to 255. |
image-tint |
( img $rrggbb -- ) |
Multiply the image colors by a color. |
sdlimg |
( z n "name" -- ) |
Handle of libSDL2_image made by shared-library. |
Raw C bindings (image)
| Word | Stack | Args |
|---|---|---|
IMG_LoadTexture |
( a z -- a ) |
2 |
Internal words (image)
| Word | Stack | What it does |
|---|---|---|
try-image |
( a n -- img, or 0 ) |
Load a texture from a file name, 0 when it fails. |
srect |
( -- a ) |
Source SDL_Rect buffer used by
draw-part. |
isize |
( -- a ) |
8 byte buffer used by image-size. |
Text (sdl2-ttf)
Type sdl2-ttf once. Text is drawn in the current
color.
| Word | Stack | What it does |
|---|---|---|
load-font |
( a n pt -- font ) |
Open a TrueType file at pt points, initializing SDL_ttf the first time. Throws on failure. |
font |
( font -- ) |
Choose the font used by text and
text-size. |
free-font |
( font -- ) |
Close the font. |
text |
( x y a n -- ) |
Draw the string with the current font and color, top left at x y. Builds a texture at each call: fine for scores and menus. |
text-size |
( a n -- w h ) |
Size the string would take with the current font. |
sdlttf |
( z n "name" -- ) |
Handle of libSDL2_ttf made by shared-library. |
Raw C bindings (text)
The SDL_Color argument of
TTF_RenderUTF8_Blended is passed packed, as
pen.
| Word | Stack | Args |
|---|---|---|
TTF_Init |
( -- n ) |
0 |
TTF_Quit |
( -- void ) |
0 |
TTF_OpenFont |
( z n -- a ) |
2 |
TTF_CloseFont |
( a -- void ) |
1 |
TTF_SizeUTF8 |
( a z a a -- n ) |
4 |
TTF_RenderUTF8_Blended |
( a z n -- a ) |
3 |
Internal words (text)
| Word | Stack | What it does |
|---|---|---|
current-font |
( -- font ) |
Value. Font chosen by font. |
tsize |
( -- a ) |
8 byte buffer used for sizes. |
Sound (sdl2-mixer)
Type sdl2-mixer once. Not to be confused with
beep, which is in the core.
| Word | Stack | What it does |
|---|---|---|
soundfont |
( a n -- ) |
Use a soundfont file (.sf2 or .sf3) for MIDI music. The name is tried as given, then without its directories in the current directory, then in the directory of the script, so a soundfont put beside the program is found from anywhere. Throws if not found. Without it FluidSynth uses the soundfont of the system. |
soundfont? |
( a n -- f ) |
Same, but returns a flag instead of throwing: true when the file was found and set. |
load-sound |
( a n -- snd ) |
Load a WAV, OGG, MP3 or FLAC effect. Opens the mixer the first time. Throws on failure. |
play |
( snd -- ) |
Play the sound on a free channel. Sounds overlap and are mixed. |
free-sound |
( snd -- ) |
Free the sound. |
sound-volume |
( n -- ) |
Volume of all channels, 0 to 128. |
stop-sounds |
( -- ) |
Stop every playing sound. |
sounds-playing |
( -- n ) |
Number of channels currently playing. |
load-music |
( a n -- mus ) |
Load a music file. Opens the mixer the first time. Throws on failure. |
music |
( mus -- ) |
Play the music, looping until stop-music. |
music-once |
( mus -- ) |
Play the music one time. |
stop-music |
( -- ) |
Stop the music. |
music-volume |
( n -- ) |
Music volume, 0 to 128. |
music? |
( -- f ) |
True while music plays. |
free-music |
( mus -- ) |
Free the music. |
sdlmix |
( z n "name" -- ) |
Handle of libSDL2_mixer made by shared-library. |
Constants (sound)
| Constant | Value |
|---|---|
MIX_RATE |
44100 |
MIX_CHANNELS |
2 |
Raw C bindings (sound)
| Word | Stack | Args |
|---|---|---|
Mix_OpenAudio |
( n n n n -- n ) |
4 |
Mix_CloseAudio |
( -- void ) |
0 |
Mix_LoadWAV_RW |
( a n -- a ) |
2 |
Mix_FreeChunk |
( a -- void ) |
1 |
Mix_PlayChannelTimed |
( n a n n -- n ) |
4 |
Mix_Volume |
( n n -- n ) |
2 |
Mix_HaltChannel |
( n -- n ) |
1 |
Mix_Playing |
( n -- n ) |
1 |
Mix_LoadMUS |
( z -- a ) |
1 |
Mix_FreeMusic |
( a -- void ) |
1 |
Mix_PlayMusic |
( a n -- n ) |
2 |
Mix_HaltMusic |
( -- n ) |
0 |
Mix_VolumeMusic |
( n -- n ) |
1 |
Mix_PlayingMusic |
( -- n ) |
0 |
Mix_SetSoundFonts |
( z -- n ) |
1 |
Internal words (sound)
| Word | Stack | What it does |
|---|---|---|
path-buf |
( -- a ) |
512 byte buffer holding the name found beside the script. |
beside-script |
( a n -- a' n' f ) |
The file name in the directory of the running script; f is true when that file exists. |
find-file |
( a n -- a' n' f ) |
Look for a file as given, then by its name alone in the current directory, then beside the script. |
mixer-init |
( -- ) |
Open the mixer at 44100 Hz, 16 bit, stereo, if not already open. |
mixer-open? |
( -- f ) |
True when the mixer is open. |
mixer-open |
( -- n ) |
Value. Mixer open flag. |
mixer-close |
( -- ) |
Close the mixer. Installed in close-hook. |
Tunes (sdl2-abc)
Type sdl2-abc once (it loads sdl2-mixer if
needed). A tune written in ABC notation (see abcnotation.com) is read,
turned into a MIDI file in a temporary file (deleted once loaded) and
played by SDL2_mixer through FluidSynth, with the soundfont given by
soundfont or the one of the system. The same words exist in
the web build, where simple oscillators play the notes.
| Word | Stack | What it does |
|---|---|---|
abc |
( a n -- ) |
Read a tune written in ABC notation (a string, several lines).
Understood: the fields K: (key and mode), L:, M:, Q:, the line
%%MIDI program n; notes A-G and
a-g with ^ _ = and
, ', lengths like 2, /2,
3/2; rests z x; chords [CEG];
ties -; broken rhythm > <; tuplets
(3; bar lines. Words, decorations, grace notes, repeats,
voices and other fields are skipped: one voice, a melody line. Sets
tune-bpm and tune-program from the tune. Use
r~ ... ~ for a multi-line string, as ABC uses the bar
character. |
tune-play |
( -- ) |
Play the tune read by abc once, as MIDI through the
soundfont. |
tune-loop |
( -- ) |
Play it again and again until tune-stop. |
tune-stop |
( -- ) |
Stop the tune. |
tune-playing? |
( -- f ) |
True while the tune plays. |
abc-play |
( a n -- ) |
abc then tune-play. |
abc-loop |
( a n -- ) |
abc then tune-loop. |
tune-notes |
( -- n ) |
Number of notes of the tune (each note of a chord counts). At most 1024 are kept. |
tune-ms |
( -- ms ) |
Length of the tune in milliseconds at tune-bpm. |
tune-bpm |
( -- n ) |
Value. Quarter notes per minute, set by Q: (default
120). Change it after abc to play faster or slower. |
tune-program |
( -- n ) |
Value. General MIDI instrument 0 to 127, set by
%%MIDI program (default 0). Change it after
abc to choose another. |
tune-midi |
( -- a n ) |
The tune as the bytes of a standard MIDI file (format 0, 480 ticks per quarter note). |
tune-save |
( a n -- ) |
Write the tune as a .mid file with that name. |
Internal words (tunes)
The reader itself lives in the vocabulary abc-int, which
is not searched by default.
| Word | Stack | What it does |
|---|---|---|
abc-int |
( -- ) |
Vocabulary holding the reader of ABC notation: about 80 words
(token, note, chord,
read-key, the arrays nt nd nk of the
notes...). Kept apart so they do not hide your own names;
also abc-int words lists them. |
smf |
( -- a ) |
32 KB buffer where the MIDI file is built. |
smf-n |
( -- n ) |
Value. Bytes written in smf. |
smf-t |
( -- n ) |
Value. Time of the last event written, in ticks. |
smf-len |
( -- n ) |
Value. Where the length of the track is written. |
sb, |
( byte -- ) |
Append a byte to the MIDI file. |
sbe32, |
( n -- ) |
Append a 32 bit number, most significant byte first. |
(vlq) |
( n flag -- ) |
Append a variable length number, high groups first. |
vlq, |
( n -- ) |
Append a MIDI variable length number. |
emit-group |
( first -- next ) |
Write the notes that start together: all on, then all off. |
build-smf |
( -- ) |
Build the MIDI file of the tune in smf. |
tune-music |
( -- mus ) |
Value. The music being played, 0 for none. |
tune-path |
( -- a n ) |
Make a new empty file with a name of its own in /tmp
(with mkstemps, so it cannot clash with another user or
run) and give its name. |
tune-load |
( -- ) |
Write the MIDI file to a temporary file, load it as music and delete the file. |
c-mkstemps |
( z n -- n ) |
C mkstemps: makes a unique file from a template ending
in XXXXXX. |
c-close |
( n -- n ) |
C close. |
tmpl |
( -- a ) |
64 byte buffer for the template and name of the temporary file. |
tune-code |
( -- n ) |
Value. Error code kept while the temporary file is deleted. |
Drawing at the prompt, and the boards (rpi, efi, web)
graphic makes the drawing words work at the prompt of
the bare-metal console (Raspberry Pi, PC UEFI), of the console window
(--gui) and of the web terminal, without
screen: a drawing area is shown under (web: above) the
text, which stays usable. 0 0 100 100 line,
red color, cls. The words below exist in the
short words of those builds; they are listed here with the others.
Drawing area
| Word | Stack | What it does |
|---|---|---|
graphic |
( -- ) |
Puts sdl2 in the search order and opens the drawing
area (gfx-on), 640 by 480 logical pixels. |
gfx-on |
( -- ) |
Opens the drawing area under the text of the console (bare metal)
and sets width, height and
gfx-mode. |
graphic-size |
( w h -- ) |
Opens the drawing area with another size, up to 1920 by 1440. The
console screen itself is 640 by 480 times console-zoom.
Web: graphic-on does the same with 640 by 480. |
gfx-mode |
( -- f ) |
Value. True (-1) while the drawing words work under the console. |
gfx-cls |
( -- ) |
At the prompt: clears the drawing area to black, whatever the pen.
cls calls it. |
graphic-on |
( -- ) |
Web only: 640 480 graphic-size. |
Keys on the boards (internal)
The serial line of the boards is read as a stream of characters,
without key releases (a USB keyboard is read by C code, see
pressed?): a key counts as held for key-hold
milliseconds after its last press.
| Word | Stack | What it does |
|---|---|---|
key-hold |
( -- n ) |
Value, default 120. How long a key counts as held after its last press, in ms. |
key-times |
( -- a ) |
Array of 256 cells: the time (ticks) of the last press
of each key code. |
key-hit |
( k -- ) |
Records the press of key k: sets last-key and
key-times. |
forget-keys |
( -- ) |
Marks every key as not pressed. |
key-within |
( ms -- k|-1 ) |
Waits at most ms for a byte from the serial line: the byte, or -1 if none comes in time. |
one-key |
( c -- ) |
Records one character c received on the serial line as a key press
(key-hit): letters become lower case, 10 is
key-enter, 127 key-backspace, ESC starts
escape. |
escape |
( -- c|-1 ) |
After an ESC: reads what follows, an arrow key or the Esc key. |
arrow |
( c -- ) |
Records the arrow key named by the letter that ends an escape
sequence: A key-up, B key-down, C
key-right, D key-left. |
ticks>ms |
( ticks -- ms ) |
Converts the duration of a note in ticks to milliseconds, from
tune-bpm (ABC tunes). |
tune-level |
( -- n ) |
Value, default 2500. Amplitude of the notes of a tune, 0 to 32767. |
tune-send |
( loop -- ) |
Sends the notes of the current tune to the mixer, once (0) or
looping (-1). Used by tune-play and
tune-loop. |
Sound on the PC (efi)
| Word | Stack | What it does |
|---|---|---|
sound-off |
( -- ) |
Stops the sound (the Intel HD Audio controller is reset) and writes
sound = off in lugforth.ini at the root of the
stick: read at boot. For a PC whose codec makes noise. |
sound sound-on |
( -- ) |
Allows the sound again, writes sound = on. The
controller starts at the next sound word. |
snd-ok |
( -- f ) |
Starts the sound if needed and tells whether a controller answered. |
Aliases
Other names for the same word, as other Forths and BASICs call them.
An alias is a plain colon definition that calls the word, so it works
the same everywhere sdl2 does (Linux, web, rpi, efi). Add
new ones here.
Where to change them: an alias is one line,
: plot ( x y -- ) dot ;, written right after the word it
renames in each of the three sources of the short words, which must stay
equal:
| Build | Source |
|---|---|
| Linux (and the console window) | ports/posix/sdl2.fs |
| Raspberry Pi and PC (UEFI) | ports/baremetal/sdl2.fs |
| Web | ports/web/sdl2.fs (the web build also has a stub below,
: plot graphic s" plot" evaluate ;, so that the word works
at the prompt before graphic) |
Then add the row to the table above, and the word to the alphabetical index at the end of this page.
Where the other sections of this page come from: this file is written
by hand, section by section, after the sources: Core, Images, Text,
Sound and Tunes (ports/posix/sdl2.fs,
sdl2_image.fs, sdl2_ttf.fs,
sdl2_mixer.fs, sdl2_abc.fs); the words of the
boards and the web page (ports/baremetal/sdl2.fs,
ports/web/sdl2.fs, ports/web/sdl2_abc.fs);
sound-off and snd-ok
(ports/efi/sound_ini.fs, ports/efi/hda.h). The
generated list of the other words is docs/WORDS.md (see
docs/dev/PITFALLS.md, "Adding words").
| Alias | Same as | Why |
|---|---|---|
plot |
dot ( x y -- ) |
Forth and BASIC programs say plot for one pixel. |
Alphabetical index
abc abc-loop abc-play
active-hook alpha arrow
audio-open audio-open? beep
beep-volume beeping? black
blue box brown
c-close c-mkstemps circle
close-screen cls color
cursor cyan delay
disc dot draw
draw-part draw-size drum-hit
dt escape events
flip font forget-keys
frame free-font free-image
free-music free-sound fullscreen
gfx-cls gfx-mode gfx-on
graphic graphic-on graphic-size
gray green height
hline image-alpha image-size
image-tint IMG_LoadTexture ink
key-backspace key-down key-enter
key-esc key-hit key-hold
key-left key-right key-space
key-tab key-times key-up
key-within last-key latency
LEFT-BUTTON line load-font
load-image load-music load-sound
magenta MIDDLE-BUTTON
Mix_CloseAudio Mix_FreeChunk
Mix_FreeMusic Mix_HaltChannel
Mix_HaltMusic Mix_LoadMUS
Mix_LoadWAV_RW Mix_OpenAudio
Mix_PlayChannelTimed Mix_Playing
Mix_PlayingMusic Mix_PlayMusic
Mix_SetSoundFonts Mix_Volume
Mix_VolumeMusic mouse-x mouse-y
music music-once music-volume
music? noise one-key
orange pen pen-a
pen-b pen-g pen-r
pink pitch-drop pixel@
play plot pressed?
purple quit! quit?
red render-hook renderer
rgb rgba RIGHT-BUTTON
sample-rate saw scan?
screen screen-open? sdl
sdl-error sdl-shutdown
SDL_ClearQueuedAudio SDL_CloseAudioDevice
SDL_CreateRenderer SDL_CreateTexture
SDL_CreateTextureFromSurface SDL_CreateWindow
SDL_Delay SDL_DestroyRenderer
SDL_DestroyTexture SDL_DestroyWindow
SDL_FreeSurface SDL_GetError
SDL_GetKeyboardState SDL_GetMouseState
SDL_GetQueuedAudioSize SDL_GetTicks
SDL_Init SDL_InitSubSystem
SDL_OpenAudioDevice SDL_PauseAudioDevice
SDL_PollEvent SDL_PushEvent
SDL_QueryTexture SDL_QueueAudio
SDL_RenderClear SDL_RenderCopy
SDL_RenderDrawLine SDL_RenderDrawPoint
SDL_RenderDrawRect SDL_RenderFillRect
SDL_RenderPresent SDL_RenderReadPixels
SDL_RenderSetLogicalSize SDL_RWFromFile
SDL_SetHint SDL_SetRenderDrawBlendMode
SDL_SetRenderDrawColor SDL_SetTextureAlphaMod
SDL_SetTextureBlendMode SDL_SetTextureColorMod
SDL_SetWindowFullscreen SDL_SetWindowTitle
SDL_ShowCursor SDL_UpdateTexture
SDL_WaitEvent sdlimg sdlmix
sdlttf setting silence
sine snd-ok sound
sound-off sound-on sound-volume
soundfont soundfont?
sounds-playing square stop-music
stop-sounds text text-size
ticks ticks>ms title
triangle TTF_CloseFont TTF_Init
TTF_OpenFont TTF_Quit
TTF_RenderUTF8_Blended TTF_SizeUTF8
tune-bpm tune-level tune-loop
tune-midi tune-ms tune-notes
tune-play tune-playing?
tune-program tune-save tune-send
tune-stop wait wave
wheel white width
window-handle yellow zoom
sdl2 short words for the Web
The web build has the same sdl2 short words as the Linux
build (SDL2.md), drawn on an HTML canvas. A game
written with them runs unchanged in both:
examples/games/sdl2_breakout.fs is loaded as is by
ports/web/sdl2_breakout.html,
examples/music/sdl2_drums.fs by
ports/web/sdl2_drums.html,
examples/music/fm/sdl2_fm.fs (an FM synthesizer with MIDI
input, see examples/music/fm/README.md) by
examples/music/fm/sdl2_fm.html,
examples/games/sdl2_roguelike.fs (with its tile sheet
examples/games/sdl2_tiles.png) by
ports/web/sdl2_roguelike.html and
examples/games/pinball/ by
ports/web/sdl2_pinball.html, and
examples/games/sdl2_raycaster.fs (a 3D maze, steered with
the mouse) by ports/web/sdl2_raycaster.html. The cells of
the web build are 32 bits: a game that works in 16.16 fixed point must
multiply through */ (see the raycaster).
At the prompt of terminal.html, as in the console window
on Linux, graphic opens a drawing area above the text
(640x480, scaled to half the height of the page at most;
w h graphic-size for another size) and the drawing words
work from the prompt, the terminal staying usable, also on a phone:
graphic, then 10 10 150 140 line,
red color 200 20 100 60 box, cls.
sdl2 alone also gives the sound words at the prompt
(s" t200 o4 c d e" play, 440 300 beep): the
first key press or tap lets the page play sounds.
sdl2 \ loads on first use, like on Linux
3 to zoom 320 240 screen \ opens a canvas over the page
: frame events black color cls red color 10 10 50 50 box flip ;
: run begin frame quit? until close-screen ;
run
Try the games
The page loads its Forth file with an XMLHttpRequest, which browsers
refuse on file://, so serve the build directory over
http:
ninja web
cd out/web && python3 -m http.server
# then open http://localhost:8000/sdl2_breakout.html
# or http://localhost:8000/sdl2_drums.html
# or http://localhost:8000/sdl2_fm.html
# or http://localhost:8000/sdl2_roguelike.html
# or http://localhost:8000/sdl2_pinball.htmlArrows, mouse or touch move the paddle. Space, click or tap launches the ball. Esc quits, and the page then offers "Play again".
In the drum machine everything can be done with the mouse: click or drag on the grid, buttons for play/stop, tempo - and + (1 bpm with the left button, 5 with the right one, hold to repeat), CLR and X (quit), and the colored pads on the left play the drums. The keyboard works too: space, up/down (5 bpm), 1 2 3 4, c, Esc.
In the roguelike, arrows or w a s d move, space waits and a click steps toward the clicked tile.
In the pinball (Night Raid), z and m or the arrows move the flippers,
down held then let go launches the ball, space or n nudges and Enter
starts a game. On a touch screen (or with ?touch in the
address) the page shows buttons over the game: the two flippers at the
bottom, LAUNCH, START and NUDGE. Each one follows its own finger and
sets the key in context.sdl.down (the same codes as the
keyboard), so both flippers can be held at once, which the single
pointer of the canvas cannot do.
What exists
These words have the same names and stack effects as on Linux, see SDL2-words.md for what each one does:
- Screen:
screen close-screen screen-open? zoom width height title fullscreen cursor - Colors:
color rgb rgba alphaand the named colorsblack white red green blue yellow cyan magenta orange purple pink brown gray - Drawing:
cls dot line box frame hline disc circle flip pixel@ - Timing:
ticks delay dt - Input:
events quit? quit! pressed? scan? last-key mouse-x mouse-y wheel, thekey-constants (key-left key-right key-up key-down key-space key-enter key-esc key-tab key-backspace) andLEFT-BUTTON MIDDLE-BUTTON RIGHT-BUTTON - Images (the words of
sdl2-image, which is a no-op here):load-image free-image image-size draw draw-size draw-part image-alpha image-tint - Beeps and hits:
beep drum-hit pitch-drop silence beeping? wave beep-volume audio-open audio-open? latency
What is different
- Not available: the raw
SDL_words,sdl2-ttf, the sound and music files ofsdl2-mixer(load-sound,play,load-music,music...), and a few helpers:wait sdl-error window-handle renderer pen pen-r pen-g pen-b pen-a sample-rate. screenputs a black layer with its own canvas over the whole page, so the terminal is hidden while the game runs. The canvas keeps its aspect ratio, shown atzoomtimes its size or smaller if the window is too small.close-screenremoves it.flipwaits for the next animation frame, so games run at the screen refresh rate, and stop while the tab is hidden.- Keys are taken from the terminal while a screen is open: they do not end up typed in it. Combinations with Ctrl, Alt or Meta and the F keys are not intercepted (Ctrl combinations are still handled by the terminal, as in any page of the web build).
pressed?also sees a key or button pressed and released between twoevents, which the Linux version misses.- Mouse and touch positions are in logical pixels, as
on Linux, and a touch counts as
LEFT-BUTTON. - On a phone, the keyboard of the device does not
come up while a screen is open: the terminal gives the focus to its
hidden text field on a tap only when no screen is open, and
screentakes the focus away from it. - Images are loaded by the browser, so any format it
reads works.
load-imagewaits for the file (the interpreter yields meanwhile) and throws if it cannot be loaded. As on Linux, a name with directories that is not found is tried again without them:s" examples/games/sdl2_tiles.png"findssdl2_tiles.pngnext to the page.image-tintmultiplies the colors, as in SDL, using a copy of the image kept for each color. - Sound starts after the first key press or click,
because browsers refuse to play sound before. Beeps before that are
dropped, and a queue more than two seconds long is not extended. A
drum-hitplays at once over the other sounds, with at most 24 sounds alive. - Tunes (
sdl2-abc, the reader of ABC notation, see SDL2.md) have the same words as on Linux:abc abc-play abc-loop tune-play tune-loop tune-stop tune-playing? tune-bpm tune-program tune-ms tune-notes, but there is no MIDI synthesizer or soundfont in a browser. Each note is played by an oscillator (the triangle wave, or the one chosen withwave), so the instrument of%%MIDI programis ignored, andtune-midiandtune-savedo not exist.soundfont?always answers false andsoundfontdoes nothing, so a program that names a soundfont runs unchanged;sdl2-mixeronly exists to be typed and givesmusic-volume. A looping tune waits for the sound to be allowed and starts at its next repeat if it was not yet. Loudness ismusic-volume(0 to 128, as on Linux). scan?takes the place of a key (KeyboardEvent.code), so a row of piano keys does not move on an AZERTY keyboard. Only letters, digits, comma, period, slash, semicolon, quote, brackets, minus, equal, space, enter, escape, tab, backspace and the arrows have a scancode here.lineuses Bresenham's algorithm and may differ by a pixel from the SDL one.byestops the web interpreter. A game file that ends withbye, like the breakout, leaves the page inert afterrunreturns (the browser console shows anobjects[op] is not a functionerror, which a plainbyein the terminal page gives as well). Reload the page to start again.
Tests
Checked with Chrome 150 without a screen, driven through the DevTools
protocol: the page loads without console errors, the breakout is drawn,
moving the pointer and holding a key move the paddle, a very short tap
on space launches the ball, a bot following the ball with the mouse
breaks bricks and schedules sound, Esc closes the screen. The drum
machine page was checked the same way: a click on the grid switches a
step, the buttons start and stop the sequencer (sounds are scheduled,
several at once), change the tempo, clear the grid and quit, and a pad
click plays its drum. The roguelike page too: the tile sheet loads
(64x8), keys and clicks play a turn without reaching the terminal,
remembered tiles are darker, monsters chase and hurt, Esc closes the
screen. Its music was checked too: the tune is read (37 notes, 21.8 s at
88 bpm), all its notes are scheduled and repeat, m stops
and restarts it, and Esc stops it. Firefox and Safari are not
tested.