sdl

MoonBit bindings for SDL

sdl
moon add klaseca/sdl@0.1.0
Download zip
Author
Version
0.1.0
License
Apache-2.0
Last updated
14 days ago
Downloads
2

Dependencies

README

#klaseca/sdl

High-level MoonBit bindings for SDL 3.

This module wraps klaseca/sdl-sys with a more MoonBit-friendly API for common application code: initialization, windows, events, trays, renderers, textures, clipboard, and message boxes.

The API is still young and may change while the binding grows.

#Installation

Add the module as a dependency from another MoonBit module:

import {
"klaseca/sdl@0.0.1",
}

Native builds also need the SDL setup required by klaseca/sdl-sys: headers, link library, and runtime shared library. Consuming this module does not require running the binding generator. npm install and npm run bindgen are only needed when changing the generated sdl-sys API in this repository.

#Application Loop

App owns SDL initialization and shutdown for simple applications.

fn main {
try! @sdl.App().run(setup)
}

fn setup(app : @sdl.App) -> Unit raise @sdl.SdlError {
let window = @sdl.Window(
title="MoonBit SDL",
width=800,
height=480,
resizable=true,
)
defer window.destroy()

let renderer = @sdl.Renderer(window)
defer renderer.destroy()

app.run_loop(() => {
renderer.set_draw_color(@sdl.Color::rgb(r=24, g=28, b=36))
renderer.clear()
renderer.present()
})
}

ControlFlow controls how the loop waits between iterations:

  • Poll: continue immediately.
  • Wait(Int): wait for an SDL event or timeout in milliseconds.
  • Exit: stop the loop.

Call app.exit() from a callback or loop body to stop the loop.

#Windows

Create a window:

let window = @sdl.Window(
title="MoonBit SDL",
width=800,
height=480,
resizable=true,
)
defer window.destroy()

Useful methods include:

  • show, hide, raise_window, minimize, maximize, restore
  • size, size_in_pixels, position, title, flags
  • set_title, set_size, set_position, set_placement, set_resizable
  • set_fullscreen, set_bordered, set_always_on_top
  • show_simple_message_box

Use WindowPlacement for SDL-managed placement modes:

window.set_placement(Centered)
window.set_placement(CenteredOnDisplay(1))

#Events

For low-level polling, use poll_event():

match @sdl.poll_event() {
Some(Quit) => ()
Some(WindowCloseRequested(event)) => println("close requested: \{event.window_id}")
Some(_) | None => ()
}

App handles SDL Quit and window close requests in its run loop. Ordinary windows close the application by default. Tray-style windows can opt into hiding on close:

let window = @sdl.Window(
title="MoonBit SDL",
width=800,
height=480,
close_behavior=Hide,
)

For window-specific event callbacks, use:

let redraw = window.on(Exposed, _event => {
renderer.clear()
renderer.present()
})
defer redraw.dispose()

WindowEvent contains the window_id, the event name, and size for events that carry window dimensions:

let resized = window.on(Resized, event => {
if event.size is Some(size) {
println("window size: \{size.width}x\{size.height}")
}
})
defer resized.dispose()

Currently supported window event names are:

  • Exposed
  • Resized
  • PixelSizeChanged
  • CloseRequested

#Rendering

Create a renderer from a window:

let renderer = @sdl.Renderer(window)
defer renderer.destroy()

renderer.set_draw_color(@sdl.Color::rgb(r=70, g=155, b=255))
renderer.clear()
renderer.fill_rect(x=280.0, y=160.0, w=240.0, h=160.0)
renderer.present()

Textures are available through Renderer::create_texture or Texture::Texture:

let texture = renderer.create_texture(
width=256,
height=256,
format=@sdl.Bgra8888,
access=@sdl.Streaming,
)
defer texture.destroy()

Use Texture::update for direct pixel uploads.

#Tray

Tray menus are built declaratively with Menu:

let tray = @sdl.Tray(
tooltip="MoonBit SDL",
menu=@sdl.Menu()
.button(label="Show window", on_click=() => window.show())
.button(label="About", on_click=() => {
window.show_simple_message_box(
title="MoonBit SDL",
message="Hello from tray",
)
})
.checkbox(label="Enabled", checked=true)
.separator()
.submenu(
label="More",
menu=@sdl.Menu().button(label="Quit", on_click=() => app.exit()),
),
)
defer tray.destroy()

If you use trays outside an SDL event-processing loop, call Tray::update() periodically.

#Clipboard

@sdl.Clipboard::set_text("Copied from MoonBit")
let text = @sdl.Clipboard::text()

#Error Handling

Most high-level operations that can fail raise SdlError.

fn open_window() -> Unit raise @sdl.SdlError {
let window = @sdl.Window(title="Example", width=640, height=480)
defer window.destroy()
}

Use normal MoonBit error propagation in functions marked raise, or try! from main for examples and experiments.

#
SdlError

pub(all) suberror SdlError {
SdlError(String)
} derive(Eq,
Debug
)

#
App

type App

#
App::App

fn App::App(init_flags? : UInt, control_flow? : ControlFlow) -> App

#
App::exit

fn App::exit(self : App) -> Unit

#
App::is_running

fn App::is_running(self : App) -> Bool

#
App::run

fn App::run(self : App, setup : (App) -> Unit raise SdlError) -> Unit raise SdlError

#
App::run_loop

fn App::run_loop(self : App, update : () -> Unit raise SdlError) -> Unit raise SdlError

#
BlendMode

pub(all) enum BlendMode {
NoBlend
Blend
BlendPremultiplied
Add
AddPremultiplied
Mod
Mul
} derive(Eq,
Debug
)

#
Clipboard

pub type Clipboard

#
Clipboard::has_text

fn Clipboard::has_text() -> Bool

#
Clipboard::set_text

fn Clipboard::set_text(text : String) -> Unit raise SdlError

#
Clipboard::text

fn Clipboard::text() -> String

#
Color

pub(all) struct Color {
r : Byte
g : Byte
b : Byte
a : Byte
} derive(Eq,
Debug
)

#
Color::rgb

fn Color::rgb(r~ : Byte, g~ : Byte, b~ : Byte) -> Color

#
Color::rgba

fn Color::rgba(r~ : Byte, g~ : Byte, b~ : Byte, a~ : Byte) -> Color

#
ControlFlow

pub(all) enum ControlFlow {
Poll
Wait(Int)
Exit
} derive(Eq,
Debug
)

#
Event

pub(all) enum Event {
Quit
KeyDown(KeyboardEvent)
KeyUp(KeyboardEvent)
MouseMotion(MouseMotionEvent)
MouseButtonDown(MouseButtonEvent)
MouseButtonUp(MouseButtonEvent)
TextInput(TextInputEvent)
WindowExposed(WindowEvent)
WindowResized(WindowEvent)
WindowPixelSizeChanged(WindowEvent)
WindowCloseRequested(WindowEvent)
Other(UInt)
} derive(Eq,
Debug
)

#
EventListener

type EventListener

#
EventListener::dispose

fn EventListener::dispose(self : EventListener) -> Unit

#
FRect

pub(all) struct FRect {
x : Float
y : Float
w : Float
h : Float
} derive(
Debug
)

#
FRect::FRect

fn FRect::FRect(x~ : Float, y~ : Float, w~ : Float, h~ : Float) -> FRect

#
FSize

pub(all) struct FSize {
width : Float
height : Float
} derive(Eq,
Debug
)

#
KeyboardEvent

pub(all) struct KeyboardEvent {
window_id : UInt
keyboard_id : UInt
scancode : Int
keycode : UInt
modifiers : UInt16
repeat : Bool
} derive(Eq,
Debug
)

pub struct Menu {
// private fields
}

fn Menu::Menu() -> Menu

fn Menu::append(self : Menu, menu : Menu) -> Menu

fn Menu::button(self : Menu, label~ : String, on_click? : () -> Unit, enabled? : Bool) -> Menu

fn Menu::checkbox(self : Menu, label~ : String, on_click? : () -> Unit, checked? : Bool, enabled? : Bool) -> Menu

fn Menu::separator(self : Menu) -> Menu

fn Menu::submenu(self : Menu, label~ : String, menu~ : Menu) -> Menu

#
MouseButtonEvent

pub(all) struct MouseButtonEvent {
window_id : UInt
mouse_id : UInt
button : Byte
clicks : Byte
x : Float
y : Float
} derive(Eq,
Debug
)

#
MouseMotionEvent

pub(all) struct MouseMotionEvent {
window_id : UInt
mouse_id : UInt
state : UInt
x : Float
y : Float
xrel : Float
yrel : Float
} derive(Eq,
Debug
)

#
PixelFormat

pub(all) enum PixelFormat {
Rgba8888
Abgr8888
Bgra8888
Bgra32
} derive(Eq,
Debug
)

#
Position

pub(all) struct Position {
x : Int
y : Int
} derive(Eq,
Debug
)

#
Properties

pub(all) struct Properties {
raw : UInt
}

#
Properties::Properties

fn Properties::Properties() -> Properties raise SdlError

#
Properties::destroy

fn Properties::destroy(self : Properties) -> Unit

#
Properties::raw

fn Properties::raw(self : Properties) -> UInt

#
Properties::set_boolean

fn Properties::set_boolean(self : Properties, name~ : Bytes, value~ : Bool?) -> Unit raise SdlError

#
Properties::set_property

fn Properties::set_property(self : Properties, property : Property) -> Unit raise SdlError

#
Properties::set_string

fn Properties::set_string(self : Properties, name~ : Bytes, value~ : String?) -> Unit raise SdlError

#
Property

pub(all) enum Property {
StringProperty(name~ : Bytes, value~ : String)
NumberProperty(name~ : Bytes, value~ : Int64)
FloatProperty(name~ : Bytes, value~ : Float)
BooleanProperty(name~ : Bytes, value~ : Bool)
} derive(Eq,
Debug
)

#
Rect

pub(all) struct Rect {
x : Int
y : Int
w : Int
h : Int
} derive(Eq,
Debug
)

#
Rect::Rect

fn Rect::Rect(x~ : Int, y~ : Int, w~ : Int, h~ : Int) -> Rect

#
Renderer

type Renderer

#
Renderer::Renderer

fn Renderer::Renderer(window : Window, name? : String) -> Renderer raise SdlError

#
Renderer::clear

fn Renderer::clear(self : Renderer) -> Unit raise SdlError

#
Renderer::create_texture

fn Renderer::create_texture(self : Renderer, width~ : Int, height~ : Int, format? : PixelFormat, access? : TextureAccess) -> Texture raise SdlError

#
Renderer::destroy

fn Renderer::destroy(self : Renderer) -> Unit

#
Renderer::fill_frect

fn Renderer::fill_frect(self : Renderer, rect : FRect) -> Unit raise SdlError

#
Renderer::fill_rect

fn Renderer::fill_rect(self : Renderer, x~ : Float, y~ : Float, w~ : Float, h~ : Float) -> Unit raise SdlError

#
Renderer::output_size

fn Renderer::output_size(self : Renderer) -> Size raise SdlError

#
Renderer::present

fn Renderer::present(self : Renderer) -> Unit raise SdlError

#
Renderer::render_texture

fn Renderer::render_texture(self : Renderer, texture : Texture, src? : FRect, dst? : FRect) -> Unit raise SdlError

#
Renderer::set_draw_color

fn Renderer::set_draw_color(self : Renderer, color : Color) -> Unit raise SdlError

#
Size

pub(all) struct Size {
width : Int
height : Int
} derive(Eq,
Debug
)

#
TextInputEvent

pub(all) struct TextInputEvent {
text : String
} derive(Eq,
Debug
)

#
Texture

type Texture

#
Texture::Texture

fn Texture::Texture(renderer : Renderer, width~ : Int, height~ : Int, format? : PixelFormat, access? : TextureAccess) -> Texture raise SdlError

#
Texture::destroy

fn Texture::destroy(self : Texture) -> Unit

#
Texture::raw

#
Texture::set_alpha_mod

fn Texture::set_alpha_mod(self : Texture, alpha : Byte) -> Unit raise SdlError

#
Texture::set_blend_mode

fn Texture::set_blend_mode(self : Texture, blend_mode : BlendMode) -> Unit raise SdlError

#
Texture::set_color_mod

fn Texture::set_color_mod(self : Texture, color : Color) -> Unit raise SdlError

#
Texture::size

fn Texture::size(self : Texture) -> FSize raise SdlError

#
Texture::update

fn Texture::update(self : Texture, pixels~ : Bytes, pitch~ : Int, rect? : Rect) -> Unit raise SdlError

#
TextureAccess

pub(all) enum TextureAccess {
Static
Streaming
Target
} derive(Eq,
Debug
)

#
Tray

type Tray

#
Tray::Tray

fn Tray::Tray(tooltip~ : String, menu? : Menu) -> Tray raise SdlError

#
Tray::append

fn Tray::append(self : Tray, menu : Menu) -> Unit raise SdlError

#
Tray::destroy

fn Tray::destroy(self : Tray) -> Unit

#
Tray::raw

#
Tray::set_tooltip

fn Tray::set_tooltip(self : Tray, tooltip : String) -> Unit

#
Tray::update

fn Tray::update() -> Unit

#
Window

type Window

#
Window::Window

fn Window::Window(title~ : String, width~ : Int, height~ : Int, placement? : WindowPlacement, hidden? : Bool, resizable? : Bool, borderless? : Bool, always_on_top? : Bool, high_pixel_density? : Bool, constrain_popup? : Bool, external_graphics_context? : Bool, fullscreen? : Bool, maximized? : Bool, minimized? : Bool, menu? : Bool, modal? : Bool, mouse_grabbed? : Bool, focusable? : Bool, opengl? : Bool, metal? : Bool, vulkan? : Bool, transparent? : Bool, tooltip? : Bool, utility? : Bool, custom_properties? : Array[Property], close_behavior? : WindowCloseBehavior) -> Window raise SdlError

#
Window::destroy

fn Window::destroy(self : Window) -> Unit

#
Window::flags

fn Window::flags(self : Window) -> UInt64

#
Window::hide

fn Window::hide(self : Window) -> Unit raise SdlError

#
Window::id

fn Window::id(self : Window) -> UInt raise SdlError

#
Window::keyboard_grab

fn Window::keyboard_grab(self : Window) -> Bool

#
Window::maximize

fn Window::maximize(self : Window) -> Unit raise SdlError

#
Window::maximum_size

fn Window::maximum_size(self : Window) -> Size raise SdlError

#
Window::minimize

fn Window::minimize(self : Window) -> Unit raise SdlError

#
Window::minimum_size

fn Window::minimum_size(self : Window) -> Size raise SdlError

#
Window::mouse_grab

fn Window::mouse_grab(self : Window) -> Bool

#
Window::on

fn Window::on(self : Window, name : WindowEventName, callback : (WindowEvent) -> Unit) -> EventListener raise SdlError

#
Window::opacity

fn Window::opacity(self : Window) -> Float raise SdlError

#
Window::position

fn Window::position(self : Window) -> Position raise SdlError

#
Window::raise_window

fn Window::raise_window(self : Window) -> Unit raise SdlError

#
Window::raw

#
Window::restore

fn Window::restore(self : Window) -> Unit raise SdlError

#
Window::set_always_on_top

fn Window::set_always_on_top(self : Window, on_top : Bool) -> Unit raise SdlError

#
Window::set_bordered

fn Window::set_bordered(self : Window, bordered : Bool) -> Unit raise SdlError

#
Window::set_close_behavior

fn Window::set_close_behavior(self : Window, behavior : WindowCloseBehavior) -> Unit raise SdlError

#
Window::set_fullscreen

fn Window::set_fullscreen(self : Window, fullscreen : Bool) -> Unit raise SdlError

#
Window::set_keyboard_grab

fn Window::set_keyboard_grab(self : Window, grabbed : Bool) -> Unit raise SdlError

#
Window::set_maximum_size

fn Window::set_maximum_size(self : Window, width~ : Int, height~ : Int) -> Unit raise SdlError

#
Window::set_minimum_size

fn Window::set_minimum_size(self : Window, width~ : Int, height~ : Int) -> Unit raise SdlError

#
Window::set_mouse_grab

fn Window::set_mouse_grab(self : Window, grabbed : Bool) -> Unit raise SdlError

#
Window::set_opacity

fn Window::set_opacity(self : Window, opacity : Float) -> Unit raise SdlError

#
Window::set_placement

fn Window::set_placement(self : Window, placement : WindowPlacement) -> Unit raise SdlError

#
Window::set_position

fn Window::set_position(self : Window, x~ : Int, y~ : Int) -> Unit raise SdlError

#
Window::set_resizable

fn Window::set_resizable(self : Window, resizable : Bool) -> Unit raise SdlError

#
Window::set_size

fn Window::set_size(self : Window, width~ : Int, height~ : Int) -> Unit raise SdlError

#
Window::set_title

fn Window::set_title(self : Window, title : String) -> Unit raise SdlError

#
Window::show

fn Window::show(self : Window) -> Unit raise SdlError

#
Window::show_simple_message_box

fn Window::show_simple_message_box(self : Window, title~ : String, message~ : String, flags? : UInt) -> Unit raise SdlError

#
Window::show_system_menu

fn Window::show_system_menu(self : Window, x~ : Int, y~ : Int) -> Unit raise SdlError

#
Window::size

fn Window::size(self : Window) -> Size raise SdlError

#
Window::size_in_pixels

fn Window::size_in_pixels(self : Window) -> Size raise SdlError

#
Window::start_text_input

fn Window::start_text_input(self : Window) -> Unit raise SdlError

#
Window::stop_text_input

fn Window::stop_text_input(self : Window) -> Unit raise SdlError

#
Window::title

fn Window::title(self : Window) -> String

#
WindowCloseBehavior

pub(all) enum WindowCloseBehavior {
ExitApp
Hide
Ignore
} derive(Eq,
Debug
)

#
WindowEvent

pub(all) struct WindowEvent {
window_id : UInt
name : WindowEventName
size : Size?
} derive(Eq,
Debug
)

#
WindowEventName

pub(all) enum WindowEventName {
Exposed
Resized
PixelSizeChanged
CloseRequested
} derive(Eq,
Debug
)

#
WindowPlacement

pub(all) enum WindowPlacement {
Positioned(Position)
Centered
CenteredOnDisplay(UInt)
Undefined
UndefinedOnDisplay(UInt)
} derive(Eq,
Debug
)

#
INIT_AUDIO

let INIT_AUDIO : UInt

#
INIT_CAMERA

let INIT_CAMERA : UInt

#
INIT_EVENTS

let INIT_EVENTS : UInt

#
INIT_GAMEPAD

let INIT_GAMEPAD : UInt

#
INIT_HAPTIC

let INIT_HAPTIC : UInt

#
INIT_JOYSTICK

let INIT_JOYSTICK : UInt

#
INIT_SENSOR

let INIT_SENSOR : UInt

#
INIT_VIDEO

let INIT_VIDEO : UInt

#
MESSAGEBOX_ERROR

let MESSAGEBOX_ERROR : UInt

#
MESSAGEBOX_INFORMATION

let MESSAGEBOX_INFORMATION : UInt

#
MESSAGEBOX_WARNING

let MESSAGEBOX_WARNING : UInt

#
TRAYENTRY_BUTTON

let TRAYENTRY_BUTTON : UInt

#
TRAYENTRY_CHECKBOX

let TRAYENTRY_CHECKBOX : UInt

#
TRAYENTRY_CHECKED

let TRAYENTRY_CHECKED : UInt

#
TRAYENTRY_DISABLED

let TRAYENTRY_DISABLED : UInt

#
TRAYENTRY_SUBMENU

let TRAYENTRY_SUBMENU : UInt

#
add_event_watch

fn add_event_watch(callback : (Event) -> Unit) -> EventListener raise SdlError

#
delay

fn delay(milliseconds : UInt) -> Unit

#
init_lib

fn init_lib(flags : UInt) -> Unit raise SdlError

#
poll_event

fn poll_event() -> Event?

#
quit

fn quit() -> Unit

Powered by MoonBit

Site sourceReport issuePackagesBuild queueSkillsStatistics

© 2026 mooncakes.io