create.core¶
The run: the Program trait, run and run_headless, the run's state (Context, Time) and its settings (WindowMode, Cursor), text entry (EditableText), source_path and DateTime.
Context¶
struct Context
The run's state: everything that outlives a frame.
Two directions share it. The loop writes the readings — time,
keyboard, mouse and the gamepads — before each update, for the
program to read; the program sets the dials, for the
loop to read. Every dial is a method, like the Canvas style setters:
context.autoscale(Autoscale.EXTEND), context.max_frame_rate(30).
Called with no arguments, each reads back the value as set, for a debug
overlay or a settings screen to show.
The dials are the run's, not the drawing's: the autoscale mode and design
size the next frame's mapping is derived from, and the ones the run loop
reads after a frame has been released, max_frame_rate, quit and
rumble. Drawing settings that outlive a frame, such as the font, the
letterbox colour and the autoclear, are on the Canvas.
Handed alongside the Canvas to Program.create, which draws frame 1,
and to every Program.update after it.
Read at frame construction. The loop re-derives the viewport from
autoscale and the design size at the top of each frame. So a dial turned
part-way through update applies to the next frame — one frame cannot
record under two mappings. Set them in create to have them hold from
frame two, or pass them to run to have them hold from frame one.
max_frame_rate, quit and rumble are the exception, and only because
the loop reads them after the frame body returns. A dial read back after such a change
reports the new value, not the one the frame in hand was built with.
Implements Copyable, Deinitable, Movable.
Fields¶
time: Time: The frame clock. The run loop ticks it before eachupdate; readdeltaandelapsedhere, and don't write it — the loop derives the next delta from it.keyboard: Keyboard: The keyboard: keys and typed text. The run loop folds each frame's events into it beforeupdate, so it is settled for the whole frame. Read it, don't write it: the loop carries it into the next frame.mouse: Mouse: The mouse: position, buttons and wheel, in screen space. Settled beforeupdateand carried between frames, likekeyboard.
Constructors¶
@staticmethod
def __init__(out self)
design_size¶
def design_size(self) -> Tuple[Int, Int]
The width and height the program is authored in: the size passed to run unless set.
def design_size(mut self, width: Int, height: Int)
Author this program in a fixed world size, scaled to any window.
Overrides the size passed to run, so a program can pin its own
coordinate space no matter how it is launched — including fullscreen,
where the window size is the display's rather than the caller's.
Takes effect on the next frame, like every dial here: the frame being
rendered keeps the mapping it was built with, since one frame cannot
record under two of them. run's width/height are the size
frame 1 is drawn at.
autoscale¶
def autoscale(self) -> Autoscale
How the design size maps onto the window. FIT unless set.
def autoscale(mut self, mode: Autoscale)
Choose how the design size maps onto the window — see Autoscale.
quit_on_escape¶
def quit_on_escape(self) -> Bool
Whether Escape stops the run loop. True unless set.
def quit_on_escape(mut self, enabled: Bool)
Whether Escape stops the run loop. On by default.
title¶
def title(self) -> String
The window's title: run's title unless set.
def title(mut self, title: String)
Retitle the window — a score, a level name, an unsaved mark.
Shows with the frame it is set in: the loop applies it before presenting, and only when it changed, so setting it every frame costs nothing. A headless run has no window and ignores it.
resizable¶
def resizable(self) -> Bool
Whether the user can resize the window: run's resizable unless set.
def resizable(mut self, enabled: Bool)
Let the user drag the window's edges to resize it, or stop them.
Applied with the frame it is set in, like title. A headless run
ignores it.
cursor¶
def cursor(self) -> Cursor
The mouse cursor over the window: ARROW unless set.
def cursor(mut self, cursor: Cursor)
Change the mouse cursor over the window — Cursor.CROSSHAIR for aiming, HAND over something clickable — or hide it with Cursor.HIDDEN, like Processing's cursor() and noCursor().
Shows with the frame it is set in, like title, and only when it
changed, so setting it every frame from a hover test costs nothing.
Set it in create to have it from the first frame. A headless run
ignores it, and so does a video driver without cursors.
lock_mouse¶
def lock_mouse(self) -> Bool
Whether the pointer is locked to the window: False unless set.
def lock_mouse(mut self, enabled: Bool)
Lock the pointer to the window, for a first-person camera or an endless drag: it is hidden and held in place, and the mouse reports only context.mouse.movement, which keeps counting where a free pointer would stop at the screen's edge. False lets it go.
Applied with the frame it is set in, like title. Unlock it for a
pause menu, where the pointer is needed again. A headless run, and a
video driver without a pointer, ignore it.
text_input¶
def text_input(self) -> Bool
Whether typing reaches context.keyboard.text: True unless set.
def text_input(mut self, enabled: Bool)
Take typed text, or stop.
On by default, so context.keyboard.text and an EditableText work
with no setup. Taking text is also what opens an input method's
composition window as the user types, and an on-screen keyboard on a
handheld or tablet — so a game played on letter keys switches it off,
and back on while a text field is in use. Keys are read the same
either way; only text goes quiet.
Applied with the frame it is set in, like title. A headless run
ignores it: there, text is whatever the program feeds in.
clipboard¶
def clipboard(self) -> String
The system clipboard's text, empty when it holds none.
Read as it changes, so this costs nothing and is the same all frame: another program's copy shows from the next frame. A headless run has a clipboard of its own, empty to start.
def clipboard(mut self, text: String)
Put text on the system clipboard, for pasting anywhere.
Reads back at once; the windowed loops hand it to the system with
the frame it is set in, like title.
window_mode¶
def window_mode(self) -> WindowMode
How the window presents itself: run's mode unless set.
def window_mode(mut self, mode: WindowMode)
Switch between windowed, fullscreen, borderless and maximized.
Applies from the next frame, like autoscale: the frame being drawn
was laid out for the window as it is, so the loop switches after
presenting it, and the next frame is drawn at the new size. Some
compositors take a frame or two longer to report it (gotcha 1 in
docs/programs.md). A window manager may refuse to maximize; a headless
run ignores it.
frame_count¶
def frame_count(self) -> Int
Frames rendered so far: 1 in create, which draws the first, and 2 during the first update.
frame_rate¶
def frame_rate(self) -> Float64
Current frames per second, derived from the last frame's delta.
0.0 on the first frame, where delta is still 0.0 and there is
no prior frame to measure against.
max_frame_rate¶
def max_frame_rate(self) -> Int
The frame-rate cap. 0 unless set, meaning uncapped.
def max_frame_rate(mut self, fps: Int) raises
Limit the loop to at most fps frames per second, or lift the cap with 0.
A cap tighter than the display's own pacing (vsync, or the CPU
backend's always-on vsync) slows the loop by sleeping at the end of
each frame; a cap looser than it does nothing, since presentation is
already waiting on the display. Not enforced by run_headless,
which has no wall clock to cap against.
quit¶
def quit(mut self)
Ask the run loop to stop after the current frame.
Unwinds normally, so the window tears down cleanly and program
destructors run — unlike std.sys.exit, which aborts the process.
gamepad¶
def gamepad(self, player: Int = Int(0)) -> Gamepad
Player player's gamepad, a copy of its state this frame — player one is 0, the default, so a one-player program needs no argument.
Each pad takes the lowest free slot as it connects and keeps it until
it disconnects. A slot no pad holds reads as a disconnected
Gamepad(), all zero and nothing down, so this never fails and needs
no check first.
rumble¶
def rumble(
mut self,
seconds: Float64,
low_frequency: Float64 = 1,
high_frequency: Float64 = 1,
player: Int = Int(0),
)
Shake player player's gamepad for seconds.
Most pads have two motors: low_frequency is the heavy one, a thud
or an engine, and high_frequency the light buzz; each is 0 (off) to
1 (full). Both at full by default, so context.rumble(0.2) is a
short jolt. A new rumble replaces the one running, so
context.rumble(0, 0, 0) stops it early. Sent after update
returns, like quit; a pad without motors, an empty slot and a
headless run ignore it.
Time¶
struct Time
Per-frame timing, ticked once per frame by the run loop.
Owns the previous tick stamp so the frame delta is derived in one place.
_start must seed that stamp before the first _tick: the window clock
counts from process start, not from zero, so an unseeded first frame would
report the whole uptime as its delta and spike anything integrating it.
Implements Copyable, Deinitable, ImplicitlyCopyable, Movable, Writable.
Fields¶
delta: Float64: Seconds since the previous frame — multiply motion by this.delta_millis: Int: Milliseconds since the previous frame, unrounded by float division.elapsed: Float64: Seconds accumulated since the first frame.elapsed_millis: Int: Milliseconds accumulated since the first frame.
Constructors¶
@staticmethod
def __init__(out self)
DateTime¶
struct DateTime
A calendar date and local time of day, read all at once.
DateTime.now() takes one reading, so its fields always agree with each
other: reading hour and minute from separate clock calls can straddle a
rollover and report 13:59 at 12:59:59.999. Unlike context.time, this is
the computer's clock, not the run's — it doesn't start at zero, and a
headless run sees the real date.
Implements Copyable, Deinitable, ImplicitlyCopyable, Movable, Writable.
Fields¶
year: Int: The full year, such as 2026.month: Int: 1 (January) to 12 (December).day: Int: Day of the month, from 1.hour: Int: 0 to 23.minute: Int: 0 to 59.second: Int: 0 to 59, or 60 during a leap second.millisecond: Int: 0 to 999, for motion smoother than whole seconds.
now¶
@staticmethod
def now() -> Self
The current local date and time, in the system's time zone.
source_path¶
def source_path(relative: String) -> String
relative, resolved against the directory of the calling source file; an absolute path is returned as it is.
Create's own loaders and savers already resolve a relative path this way
(Image.load("assets/image.png")), so this is for paths handed to
anything else: the standard library's open(), listdir, makedirs.
Called from a helper, it resolves against the helper's file — the same
rule as Python's __file__.
The caller's path is baked in at compile time, which is why this has to be
inlined: call_location reports the site it is inlined into. So mojo run
and mojo build agree on the answer, and since the compiler makes the
path absolute, a built binary finds its files from any directory, as long
as they stay where they were at build time.
Program¶
trait Program
What run and run_headless drive: create, which draws the first frame, then update once per frame after it.
One per-frame method, not two. A separate render would have to be handed
a canvas it may not write to and no input at all, which is what forced a
program to smuggle a decision from one into the other through a field —
reading a key in update to file a screenshot in render, or caching a
frame rate reading to render it. Deciding and rendering are the same frame's
work, so they are the same method's.
There are no event callbacks. Input arrives on the context, as
context.keyboard, context.mouse and context.gamepad(player), and
nowhere else — so there is one place a frame's
decisions are made and no ordering question between a callback and the
frame body.
Implements Deinitable, Movable.
create¶
@staticmethod
def create(mut context: Context, mut canvas: Canvas) raises -> Self
Build the program and draw frame 1, as Processing's setup does.
Where resources the program drives on its own schedule are
constructed — images, fonts, sounds, an Audio device — and where
canvas settings that outlive the frame, such as canvas.autoclear,
are set once.
canvas is a real frame, presented like every later one: what is
drawn here shows, and geometry is as valid as on any first frame (see
gotcha 1 in docs/programs.md). The design size and autoscale mode are
run's arguments, already in place; context.design_size or
context.autoscale called here applies from the next frame, like any
dial. context.time is zero and no key or button is down;
context.frame_count() is 1.
Drawing that needs the program's own fields builds Self into a
local first, draws, then returns it.
update¶
def update(mut self, mut context: Context, mut canvas: Canvas) raises
Advance the program by one frame, and render it.
Two parameters, two lifetimes. context is the run's state: it
outlives the frame, carries what the loop measured for this one —
context.time and the devices — and takes the dials for the next
one — the autoscale mode, the clear, quit(). canvas is where this
frame is drawn: it is built fresh, rendered on, and dropped before
presentation, so it must not be stored anywhere. The first update
draws frame 2; create drew frame 1.
WindowMode¶
struct WindowMode
Implements Copyable, Deinitable, Equatable, ImplicitlyCopyable, Movable, Writable.
Constants¶
WINDOWED = WindowMode(Int(0))FULLSCREEN = WindowMode(Int(1))BORDERLESS = WindowMode(Int(2))MAXIMIZED = WindowMode(Int(3))
Fields¶
value: Int
Constructors¶
@staticmethod
def __init__(out self, value: Int)
__eq__¶
def __eq__(self, other: Self) -> Bool
__ne__¶
def __ne__(self, other: Self) -> Bool
Cursor¶
struct Cursor
Implements Copyable, Deinitable, Equatable, ImplicitlyCopyable, Movable, Writable.
Constants¶
ARROW = Cursor(Int(0)): The ordinary pointer.CROSSHAIR = Cursor(Int(1)): Precise aiming or drawing: Processing'sCROSS.HAND = Cursor(Int(2)): Something clickable, like a link.TEXT = Cursor(Int(3)): Text that can be edited or selected: an I-beam.WAIT = Cursor(Int(4)): Busy, not taking input.PROGRESS = Cursor(Int(5)): Busy, but still taking input: usually an arrow with a spinner.MOVE = Cursor(Int(6)): Something that can be dragged in any direction.NOT_ALLOWED = Cursor(Int(7)): An action that can't be done here.RESIZE_HORIZONTAL = Cursor(Int(8))RESIZE_VERTICAL = Cursor(Int(9))RESIZE_DIAGONAL_RISING = Cursor(Int(10))RESIZE_DIAGONAL_FALLING = Cursor(Int(11))HIDDEN = Cursor(Int(12)): No cursor over the window: Processing'snoCursor().
Fields¶
value: Int
Constructors¶
@staticmethod
def __init__(out self, value: Int)
__eq__¶
def __eq__(self, other: Self) -> Bool
__ne__¶
def __ne__(self, other: Self) -> Bool
from_name¶
@staticmethod
def from_name(name: String) -> Optional[Self]
The cursor whose constant is name, case folded: "hand", "crosshair", "hidden". None for an unknown name — for a binding read from a file, where a misspelled constant would have failed to compile.
run¶
def run[P: Program](
title: String,
mode: WindowMode = WindowMode.WINDOWED,
width: Int = Int(1280),
height: Int = Int(720),
backend: RenderBackend = RenderBackend.CPU,
resizable: Bool = True,
antialiasing: Antialiasing = Antialiasing.MEDIUM,
autoscale: Autoscale = Autoscale.FIT,
) raises
Open a window and run P in it until it quits.
width/height are the design size: the space the program is
authored in, and the one canvas.width()/height(), the canvas edges and
context.mouse.position are reported in. They are not a window size that happens to double as one: a
WINDOWED or BORDERLESS launch opens a window of that size because the
two coincide there, while FULLSCREEN and MAXIMIZED take the display or
its work area and the design is scaled onto it. mode=FULLSCREEN with a
size therefore means author at that size, present fullscreen.
The scaling is autoscale, Autoscale.FIT by default, so a program keeps
its layout on any display:
| call | FIT / EXTEND | OFF |
|---|---|---|
run("T", width=1000, height=1000) |
design 1000x1000, scaled | world = window |
run("T", FULLSCREEN, width=1000, height=1000) |
design 1000x1000, scaled | world = monitor |
run("T", WindowMode.FULLSCREEN) |
design 1280x720, scaled | world = monitor |
Autoscale.OFF is the opt-out, and the only way the size here stops
meaning anything: the design size goes unused and coordinates become
the window's own pixels, which is how a program authors against the
display rather than against a fixed space. context.autoscale(mode)
switches it later in the run.
context.design_size(w, h) pins the same space from inside create, which
is where a program with an opinion of its own states it. The size here is
the shorthand for the common case where the window and the design agree.
backend=RenderBackend.GPU runs the same program through the GL backend
instead — a different window, a different loop, and the same frame. It is
a branch rather than a value the loop holds because Mojo 1.0 has no
dynamic trait dispatch, which is also why Backend switches on a kind.
antialiasing smooths the edges of shapes on either backend
(Antialiasing.MEDIUM by default, OFF for hard pixels). See
Antialiasing.
run_headless¶
def run_headless[P: Program](
width: Int,
height: Int,
frames: Int = Int(1),
window_size: Optional[Tuple[Int, Int]] = None,
backend: RenderBackend = RenderBackend.CPU,
antialiasing: Antialiasing = Antialiasing.MEDIUM,
autoscale: Autoscale = Autoscale.FIT,
) raises -> Image
Run P without a window — create's frame, then frames calls to update — and return the last frame as an Image, read back with pixel(x, y) in image coordinates (top-left origin, y down).
The same sequence as run, minus the window: create draws frame 1,
then update draws one frame per call, with the letterbox painted after
each. frames=0 returns create's frame alone. width
and height are the design size; window_size is the pixel size of
the window it stands in for, the design size when None — pass a
different shape to exercise autoscale, since a design that matches the
window maps 1:1 and leaves no bars. The returned image is that size.
Time is synthetic and the input is empty, so the result depends only on
the program. A program that feeds context.keyboard or context.mouse
itself (a test pressing keys) gets the windowed loops' frame boundaries: a press,
release or typed text lasts the frame it was fed in, while a key held
down stays down.
backend=RenderBackend.GPU runs the same frames through the GL backend
into an offscreen framebuffer instead, reading the result back at the
end rather than replaying onto the buffer every frame — see
_headless_gl.mojo. It raises if no GL context can be created; it never
falls back to the CPU backend.
antialiasing is run's, with the same default, and reaches both
backends as it does in a window.
autoscale is run's too: how the design maps onto the framebuffer.
EditableText¶
struct EditableText
A line of text being typed into, with a caret and a selection: the state of a text field, without its look.
The program owns one per field and feeds it the frame each update,
then draws text itself — measuring with canvas.text_width puts the
caret after the right character and the selection behind the right
ones:
self.name.update(context) # in update
canvas.text_alignment(Align.LEFT)
var selected_x = left + canvas.text_width(self.name.before_selection())
canvas.rectangle(...) # from selected_x, text_width(selected_text()) wide
canvas.text(self.name.text, (left, 0))
var caret_x = left + canvas.text_width(self.name.before_caret())
canvas.line((caret_x, -20), (caret_x, 20))
update replays the frame's typing in the order it came, so text and
editing keys typed within one frame land where they were typed:
| Keys | Does |
|---|---|
| Typed text | Replaces the selection, or goes in at the caret |
| Backspace, Delete | Delete the selection, or one character either side |
| Left, Right | Move one character, or to a selection's edge |
| Home, End | Move to the start or the end |
| word + Left/Right/Backspace/Delete | The same, a word at a time |
| Shift + any move | Extends the selection instead |
| command + A, C, X, V | Select all, copy, cut, paste |
Command is Cmd on macOS and Ctrl elsewhere; word is Option on macOS and Ctrl elsewhere, and on macOS Cmd+Left/Right go to the start and the end, as the system's own fields do. The command shortcuts follow the letter printed on the key, so they are where the user expects on any layout. Moves and deletes repeat while held; Enter is left to the program, which decides what submitting means. Clicking into the text is the program's: it knows where the text is drawn.
max_length, if set, is the most characters typing and pasting can
bring the text to. Every edit and move is by whole characters, so a
multi-byte UTF-8 character is never split. text is a plain field:
reading it is the point, and assigning it (self.name.text = "") is
safe, since the caret and the selection are kept inside it and on
character boundaries.
Implements Copyable, Deinitable, Movable, Writable.
Fields¶
text: String: What has been typed so far, UTF-8.max_length: Optional[Int]: The most characters typing and pasting can bringtextto;Nonefor no limit. Text assigned directly is kept as it is.
Constructors¶
@staticmethod
def __init__(out self, text: String = "", *, max_length: Optional[Int] = None)
Start with text already typed, the caret after it.
update¶
def update(mut self, mut context: Context)
Apply one frame's typing, in the order it was typed. Takes the whole Context for its clipboard, which copy, cut and paste use.
before_caret¶
def before_caret(self) -> String
The text left of the caret — what to measure to place it.
after_caret¶
def after_caret(self) -> String
The text right of the caret.
selected_text¶
def selected_text(self) -> String
The selected text, empty when nothing is.
before_selection¶
def before_selection(self) -> String
The text left of the selection — what to measure to place its highlight. The same as before_caret when nothing is selected.
insert¶
def insert(mut self, typed: String)
Put typed in place of the selection, or at the caret, and move the caret past it. Cut short at max_length.
delete_backward¶
def delete_backward(mut self)
Delete the selection, or the character left of the caret, as Backspace does.
delete_forward¶
def delete_forward(mut self)
Delete the selection, or the character right of the caret, as Delete does.
delete_word_backward¶
def delete_word_backward(mut self)
Delete the selection, or back to the start of the word left of the caret.
delete_word_forward¶
def delete_word_forward(mut self)
Delete the selection, or on to the end of the word right of the caret.
move_left¶
def move_left(mut self, *, select: Bool = False)
Move the caret one character left, stopping at the start. With a selection and not select, go to its start instead.
move_right¶
def move_right(mut self, *, select: Bool = False)
Move the caret one character right, stopping at the end. With a selection and not select, go to its end instead.
move_word_left¶
def move_word_left(mut self, *, select: Bool = False)
Move the caret to the start of the word on its left.
move_word_right¶
def move_word_right(mut self, *, select: Bool = False)
Move the caret to the end of the word on its right.
move_to_start¶
def move_to_start(mut self, *, select: Bool = False)
move_to_end¶
def move_to_end(mut self, *, select: Bool = False)
select_all¶
def select_all(mut self)
cut¶
def cut(mut self, mut context: Context)
Put the selection on the clipboard and delete it.
paste¶
def paste(mut self, context: Context)
Insert the clipboard's text, as if typed. A line field takes one line, so a newline pastes as a space.