Implemented in src/lua/luaappapi.cpp.
Application-level primitives — concerns that belong to the running app/game itself, not to any one engine subsystem (camera, sound, sprites, ...).
app_set_name(name)Set the application window's title. Scarab (the engine) has no
opinion on what a game calls itself — the window opens with a
generic default title ("Scarab", main.h's APP_NAME)
before any Lua exists to override it, since the window has to
exist before main.lua's first line runs. Call this once,
early, with the game's real name:
-- main.lua
app_set_name( "Caravellius" )
screen_fade(alpha)Set a whole-screen fade overlay, drawn on top of every other
rendered element (tilemap, sprites, everything) — not a
per-texture effect. alpha ranges from 0.0 (fully visible,
no overlay) to 1.0 (fully black). Intended for
stage-start/stage-end and game-over transitions: ramp it over
several frames from on_update to fade in/out rather than
jumping straight to an endpoint.
-- fade in from black over ~1 second at 60fps
local __FADE_STEP = 1 / 60
local fade_alpha = 1.0
function on_update( dt )
if fade_alpha > 0 then
fade_alpha = math.max( 0, fade_alpha - __FADE_STEP )
screen_fade( fade_alpha )
end
end
app_set_fullscreen(fullscreen, strategy) / app_get_fullscreen()Enter/leave fullscreen, or query the current state. The game always renders at it's own fixed internal resolution regardless of the actual window/monitor size — the engine handles scaling and letterboxing (black bars, preserving aspect ratio) to fit whatever the real window size ends up being, whether that's from this toggle or the player manually resizing the window. Nothing else needs to account for it.
strategy (optional, sunlight v0.14.0+) picks which of two
fullscreen strategies to enter with:
FULLSCREEN_STRATEGY_REAL (the default when strategy is
omitted) — a genuine OS-level fullscreen space. On macOS
this is the strategy that actually gets the Dock/menu bar
to hide automatically; a real bug found live in Caravellius
(the Dock drawing on top of the game window) is what
prompted sunlight to switch its own default to this.FULLSCREEN_STRATEGY_BORDERLESS_WINDOWED — the older
behavior (an ordinary window resized to the monitor's
native resolution, no real fullscreen space entered) — kept
available as a fallback for any platform/window manager
where a true video-mode switch misbehaves.Switching strategy while already fullscreen in the other
one is unsupported — call app_set_fullscreen(false) first,
then re-enter fullscreen with the new strategy.
-- toggle fullscreen on a key press (edge-triggered, not held) - defaults to real fullscreen
if input_is_key_released( KEY_F5 ) then
app_set_fullscreen( not app_get_fullscreen() )
end
-- explicitly ask for the older borderless-windowed behavior instead
app_set_fullscreen( true, FULLSCREEN_STRATEGY_BORDERLESS_WINDOWED )
app_set_target_fps(fps) / app_get_target_fps()Set (or query) the renderer's own target frame rate — the cap
the game loop paces itself against, not the current
measured FPS (see app_get_draw_fps's own on-screen
counter, right below, for that). Scarab's own default is
main.h's FRAMES_PER_SECOND (60 today), applied once at
boot; this primitive is a genuine live change, taking effect
immediately, in either direction:
-- halve the target frame rate at runtime
app_set_target_fps( app_get_target_fps() / 2 )
app_set_draw_fps(draw_fps) / app_get_draw_fps()Show or hide the on-screen FPS counter (raylib's own, drawn
at the top-left corner of the window every frame), or query
the current state. Scarab's own default (main.cpp) is
false — a generic engine-level choice, not a game one.
Caravellius overrides it to true, early in main.lua:
-- main.lua
app_set_draw_fps( true )
app_set_window_resizeable(resizeable) / app_get_window_resizeable()Allow or disallow the user resizing the window by dragging
its edges/corners, or query the current state. A genuine
live toggle — safe to call both before the window exists
and at any point after, in either direction (unlike raylib's
own pre-InitWindow-only FLAG_WINDOW_RESIZABLE config
flag). Scarab's own default (main.cpp) is true;
Caravellius overrides it to false (the game renders at a
fixed internal resolution with letterboxing, so a resizeable
window offers no benefit):
-- main.lua
app_set_window_resizeable( false )
app_set_stretch_to_fill(stretchToFill) / app_get_stretch_to_fill()Choose how the engine's fixed internal render resolution
(DISPLAY_WxDISPLAY_H, 1260x920 today — what
screen_get_width/screen_get_height report, unrelated to
the real window size) gets blitted onto the real
window/screen whenever the two sizes differ (fullscreen, or
a live-resized window). Recomputed every frame, so it applies
immediately regardless of when it's called.
false (the engine's own default, both Scarab's and
Caravellius's) — letterbox: preserves the render
target's own aspect ratio via one uniform scale factor,
filling whichever axis doesn't fit exactly with black bars.true — stretch-to-fill: fills the entire
window/screen with no black bars at all, using independent
X/Y scale factors — the image visibly warps
(stretches/squishes) whenever the window's own aspect
ratio doesn't match the render target's 1260:920.app_set_stretch_to_fill( true ) -- fill the window completely, no letterboxing
draw_filled_rectangle(x, y, width, height, r, g, b, a)Draw a filled, solid-color rectangle in screen space — same
coordinate system as draw_text/screen_get_width (the
fixed 1260x920 design resolution, independent of the live
window size, fullscreen state, or app_set_stretch_to_fill),
not a world-space/camera-relative one. Meant for simple HUD
elements — a progress/health bar, a meter — that don't
warrant a whole sprite/texture asset. A thin pass-through to
ITileMap::DrawFilledRectangle, itself a thin pass-through
to IEngine::DrawFilledRectangle — the same underlying call
the whole-screen screen_fade overlay already uses
internally, just exposed here as it's own
arbitrary-position/size/color primitive rather than that
fixed, full-window, single-alpha-value overlay.
-- a simple health bar: a dark background plate, then a colored fill
-- proportional to some fraction (0.0-1.0)
draw_filled_rectangle( x, y, width, height, 40, 40, 40, 255 )
draw_filled_rectangle( x, y, math.floor( width * fraction ), height, 220, 40, 40, 255 )
app_get_platform()Query which host platform Scarab was compiled for —
PLATFORM_WINDOWS, PLATFORM_MACOS, PLATFORM_LINUX, or
PLATFORM_UNKNOWN (compiled for something other than those
three). A pure compile-time fact
(_WIN32/__APPLE__/__linux__ preprocessor macros), not a
runtime probe — the result never changes for the lifetime of
a given built executable, and there's no dependency on
IEngine/raylib/GLFW or any other engine-owned state.
Useful for any platform-conditional Lua behavior a game
needs — for example, picking which app_set_fullscreen
strategy to default to per platform. FULLSCREEN_STRATEGY_REAL
causes a measured performance regression on Linux. Windows
isn't affected by that specific issue, but
FULLSCREEN_STRATEGY_REAL is kept macOS-only project-wide
rather than per-platform — Linux and Windows both use
FULLSCREEN_STRATEGY_BORDERLESS_WINDOWED here by deliberate
choice, not because Windows itself has a problem with
FULLSCREEN_STRATEGY_REAL:
local platform = app_get_platform()
if platform == PLATFORM_MACOS then
app_set_fullscreen( true, FULLSCREEN_STRATEGY_REAL )
else
app_set_fullscreen( true, FULLSCREEN_STRATEGY_BORDERLESS_WINDOWED )
end
app_quit()Requests the game close, the programmatic equivalent of the
player pressing the configured exit key (ESC by default)
or clicking the window's close button. Takes effect on the
next frame, not immediately — safe to call from anywhere
on_update/an engine callback reaches, including mid-frame;
whatever's already queued to draw this frame still finishes
drawing normally before the game actually closes.
There was no programmatic way to quit at all before this — every prior sample/tool worked around that by just telling the player to close the window manually.
function on_update(dt)
if quit_button_pressed then
app_quit()
end
end
app_set_exit_key(key) / app_get_exit_key()Sets which key, when pressed, triggers the default
quit-the-application behavior (app_quit()'s own effect,
but from a raw keypress rather than a direct call) —
KEY_ESCAPE (see the "Keyboard keys" constants on
input.md) by default. Pass KEY_NULL to disable this
entirely, so
pressing ESC no longer closes the game on its own —
useful for a game that wants ESC to mean something else
instead (a back/cancel action in its own menu system, for
example), handled entirely in Lua by polling
input_is_key_released(KEY_ESCAPE) each frame, rather than
an un-interceptable quit racing ahead of it.
-- disable the default ESC-quits behavior; this game's own
-- Lua code decides what ESC does instead
app_set_exit_key( KEY_NULL )
app_get_time()Returns the real, wall-clock time in seconds (a fractional
number, sub-millisecond precision) elapsed since the game
window was initialized. Monotonic — never jumps backward,
and unaffected by the system clock changing — and entirely
independent of the per-tick dt passed to on_update,
which is a fixed 1000 / 60 (truncated to 16) rather than
a measured frame time, so accumulating it drifts slow
against real time, and drifts further on any machine that
can't sustain the target frame rate.
Use this instead of accumulating dt for anything that has
to track real elapsed time at sub-second precision — a
typewriter-style text reveal, a cutscene timeline meant to
line up with a song. (os.time() is the other real-time
source, but only has whole-second granularity.) Anchor off
a start timestamp and compare against it each frame, rather
than counting up.
Under --headless (the null renderer) this is a virtual
clock instead: each frame advances it by exactly
1 / target FPS, however fast the frames actually run, so a
long timeline can be replayed in a fraction of the time.
-- reveal one character every 50ms of real time, regardless
-- of the actual frame rate
local CHAR_SECONDS = 0.05
local text = "Hello, world!"
local start = app_get_time()
function on_update(dt)
local shown = math.floor( ( app_get_time() - start ) / CHAR_SECONDS )
draw_text( text:sub( 1, shown ), 16, 16, 16, 255, 255, 255, 255 )
end
The strategy argument app_set_fullscreen optionally
takes — see that primitive's own doc for what each one
actually does and why FULLSCREEN_STRATEGY_REAL is
kept macOS-only project-wide.
FULLSCREEN_STRATEGY_REAL, FULLSCREEN_STRATEGY_BORDERLESS_WINDOWED
The values app_get_platform() returns.
PLATFORM_WINDOWS, PLATFORM_MACOS, PLATFORM_LINUX, PLATFORM_UNKNOWN