Skip to content

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 each update; read delta and elapsed here, 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 before update, 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 before update and carried between frames, like keyboard.

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's CROSS.
  • 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's noCursor().

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 bring text to; None for 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.