Sign in

    orby

    Native MoonBit application and window host.

    moonbit
    windowing
    win32
    gtk
    Download zip
    Version
    0.1.0-beta.6
    License
    Apache-2.0
    Last updated
    2 months ago
    Downloads
    366

    #orby

    Orby is a native MoonBit application and window host for Win32 and GTK3/GDK. It owns windows and their UI event loop through the common Nanaloveyuki/orby package, then exposes embedding hosts for native consumers such as MoonView.

    Nanaloveyuki/orby is not compatible with Tao, winit, or Tauri.

    #API

    Import Nanaloveyuki/orby; its App, EventLoop, ActiveApp, Window, events, monitor types, and WebViewHost are the cross-platform public API. The windows and linux packages are backend implementation details and are not application-facing APIs.

    All Orby calls and callbacks belong to the UI thread. Workers can use EventLoop::proxy to submit copied byte messages; Orby delivers them through App::proxy_message on the UI thread.

    Only one EventLoop may be active per process. Create another only after the previous run_app returns or its ExternalAppLoop has terminated.

    App::started may raise AppError::StartupFailed. For asynchronous setup or runtime failures, call ActiveApp::fail(reason) from a UI callback. The loop stops, invokes App::exiting once, and EventLoop::run_app raises AppError::RuntimeFailed. Destroy a child runtime such as MoonView before its parent Window; Orby does not reverse application-owned teardown ordering.

    #Usage

    Import the root package and keep the created window until it is destroyed:

    import {
    "Nanaloveyuki/orby",
    }

    struct Example {
    mut window : @orby.Window?
    }

    pub impl @orby.App for Example with fn started(self, app) {
    self.window = Some(
    try! app.create_window(@orby.WindowOptions::new(title="Orby example")),
    )
    }

    pub impl @orby.App for Example with fn window_event(self, _app, _id, event) {
    match event {
    @orby.WindowEvent::CloseRequested =>
    match self.window {
    Some(window) => window.destroy()
    None => ()
    }
    _ => ()
    }
    }

    fn main raise {
    let event_loop = @orby.EventLoop::new()
    ignore(event_loop.run_app({ window: None }))
    }

    #Status

    The 0.1.0-beta.0 pre-release targets a small application lifecycle, native windows, resize/DPI/focus/redraw events, and WebView-ready host containers. MoonView integration is validated on both supported platforms in CI. Public API changes remain possible before the stable 0.1.0 release.

    CloseRequested is application-controlled: call Window::destroy from the event handler to accept it, or do nothing to cancel it. request_close uses the same path for programmatic closure. Destroying the final window ends the event loop automatically; ActiveApp::exit_with_code can set a process result. Calls on a destroyed Window are safe no-ops; use is_destroyed before retaining or reusing a window handle across lifecycle callbacks.

    Commands on a destroyed window are no-ops. Queries, webview_host, and WebViewHost::native_handle require a live native window and raise WindowError::Destroyed. Destroy the MoonView instance before its Orby window.

    Window Size values are physical client-area pixels. Use LogicalSize with a window's scale_factor for device-independent layout, and use inner_size / set_inner_size when sizing native content. set_outer_position is a request that may be ignored by a Wayland compositor.

    Window::focus restores, shows, and requests activation for a live top-level window. Platform foreground policy remains authoritative.

    Keyboard and pointer input arrive through WindowEvent. NativeKeyEvent.code is intentionally backend-native, while TextInput carries printable Unicode text and modifiers use Shift, Control, Alt, and Meta. IME composition, touch, and raw device events remain outside the current API.

    #Documentation

    • Development: local setup, project requirements, and validation.
    • Root package guide: complete consumer API, lifecycle, window, input, display, and WebView-host documentation.
    • MoonView integration: versioned desktop host setup, lifecycle, resize, failure, and teardown requirements.
    • Contributing: pull request process.
    • Releasing: version and package release checklist.

    #Orby Root Package

    Nanaloveyuki/orby is the consumer-facing package for native MoonBit desktop applications. It creates and owns Win32 or GTK3/GDK windows, dispatches their events on the UI thread, and provides a native child host for embedding a runtime such as MoonView.

    Import this package rather than Nanaloveyuki/orby/windows or Nanaloveyuki/orby/linux; those packages are backend implementation details.

    #Platform and Thread Model

    Orby currently supports Windows and Linux with GTK3/GDK. Create the EventLoop, create windows, and call every Window, WebViewHost, and ActiveApp method from the same UI thread. Workers can use EventLoop::proxy or ActiveApp::proxy to submit Bytes; App::proxy_message receives each message on the UI thread in FIFO order.

    The proxy copies messages into a native queue. A message is limited to 1 MiB and the queue to 8 MiB; post returns MessageTooLarge, QueueFull, or Closed instead of blocking a worker.

    EventLoop::new raises InitError when the native host cannot initialize. On Windows this requires an STA UI thread; on Linux it requires a graphical GTK3 session. Only one EventLoop may be active in a process; construct the next one only after run_app returns or ExternalAppLoop::terminate completes.

    #Minimal Application

    Keep each created Window in application state. A close request is only a request: destroy the window from window_event to accept it.

    import {
    "Nanaloveyuki/orby",
    }

    struct Example {
    mut window : @orby.Window?
    }

    pub impl @orby.App for Example with fn started(self, app) {
    self.window = Some(
    try! app.create_window(
    @orby.WindowOptions::new(
    title="Orby example",
    size=@orby.Size::new(width=960, height=640),
    ),
    ),
    )
    }

    pub impl @orby.App for Example with fn window_event(self, _app, _id, event) {
    match event {
    @orby.WindowEvent::CloseRequested =>
    match self.window {
    Some(window) => window.destroy()
    None => ()
    }
    _ => ()
    }
    }

    fn main raise {
    let loop = @orby.EventLoop::new()
    ignore(loop.run_app({ window: None }))
    }

    Destroying the final native window exits the event loop. Call ActiveApp::exit or exit_with_code when application state, rather than a window, decides termination.

    #Lifecycle and Failures

    App receives callbacks in this order:

    1. started runs after native callback installation and can create windows.
    2. window_event receives per-window activity; about_to_wait runs after a native event batch.
    3. proxy_message receives worker-submitted byte messages on the UI thread.
    4. exiting runs exactly once after native loop cleanup.

    Use AppError::StartupFailed(reason) from started when setup cannot continue. EventLoop::run_app finishes native cleanup, invokes exiting, and then raises the same startup error.

    For a failure discovered asynchronously in a UI callback, call ActiveApp::fail(reason). The first reason wins, new application callbacks are suppressed, and run_app raises AppError::RuntimeFailed(reason) after calling exiting.

    pub impl @orby.App for Example with fn about_to_wait(self, app) {
    if startup_work_failed() {
    app.fail("initial runtime setup failed")
    }
    }

    fn run_example() raise {
    let loop = @orby.EventLoop::new()
    let app = { window: None }
    try ignore(loop.run_app(app)) catch {
    @orby.AppError::StartupFailed(reason) => println("startup: \{reason}")
    @orby.AppError::RuntimeFailed(reason) => println("runtime: \{reason}")
    }
    }

    Do not use a blocking operation from about_to_wait, a window callback, or any other UI-thread callback.

    #Windows, Geometry, and Displays

    WindowOptions configures title, physical client size, visibility, and resizability at creation. Size is always physical client pixels; LogicalSize::to_physical and Size::to_logical convert with an explicit, validated scale factor.

    Live windows provide title, visibility, resizability, decoration, minimization, maximization, fullscreen, client-size constraints, redraw, close requests, and outer-position requests. A Wayland compositor may ignore an outer-position request. focus requests that a live window is restored, shown, and activated; the window manager may still limit foreground activation. set_fullscreen_on accepts a MonitorId from the current monitor snapshot.

    EventLoop::available_monitors, primary_monitor, and monitor expose current display snapshots. A Window also has current_monitor and scale_factor queries.

    Commands on a destroyed Window are no-ops. Queries and host acquisition are checked: inner_size, scale_factor, fullscreen/minimized/maximized state, current_monitor, webview_host, and WebViewHost::native_handle raise WindowError::Destroyed after destruction. create_window can raise WindowError::CreationFailed; set_fullscreen_on can raise WindowError::MonitorUnavailable.

    #Events and Input

    WindowEvent reports close and destruction, resize, scale-factor changes, focus, redraw requests, native key input, printable text input, pointer motion, pointer enter/leave, mouse buttons, and smooth wheel deltas.

    NativeKeyEvent.code intentionally stays backend-native. Use TextInput for printable text. IME composition, touch, raw device input, cursor control, and drag-and-drop are not part of the current API.

    #WebView Hosts

    Call Window::webview_host while the window is live, then pass WebViewHost::native_handle to a child runtime that accepts a native parent handle. Resize the child in response to WindowEvent::Resized and destroy the child runtime before calling Window::destroy.

    Orby owns neither MoonView configuration nor its asynchronous WebView event contract. This release resolves the published Nanaloveyuki/moonview@0.1.0-beta.9 package, which in turn resolves AJNI 0.2.3. Follow the MoonView integration guide for creation, event, resize, resource-limit, failure, and teardown handling.

    Orby has no Android backend. Android hosts should use MoonView's Android API directly rather than treating an Orby WebViewHost as an Android surface.

    #Public API Map

    • EventLoop initializes the host, runs App, and queries displays.
    • ActiveApp creates windows, controls loop scheduling, exits, or reports a checked runtime failure.
    • Window owns one native window; WebViewHost exposes its live child host.
    • WindowOptions, Size, LogicalSize, Position, Monitor, and MonitorId describe creation and display state.
    • WindowEvent, NativeKeyEvent, Modifiers, MouseButton, and ScrollDelta describe input and native window activity.
    • InitError, GeometryError, WindowError, and AppError describe checked failures.

    App

    pub(open) trait App {
    fn started(Self, ActiveApp) -> Unit raise AppError = _
    fn window_event(Self, ActiveApp, WindowId, WindowEvent) -> Unit = _
    fn proxy_message(Self, ActiveApp, Bytes) -> Unit = _
    fn about_to_wait(Self, ActiveApp) -> Unit = _
    fn exiting(Self, ActiveApp) -> Unit = _
    }

    Application callbacks run only on Orby's UI thread.

    AppError

    pub(all) suberror AppError {
    StartupFailed(String)
    RuntimeFailed(String)
    } derive(
    Debug
    )

    GeometryError

    pub(all) suberror GeometryError {
    InvalidScaleFactor(Double)
    } derive(
    Debug
    )

    InitError

    pub(all) suberror InitError {
    AlreadyActive
    UnsupportedHost(String)
    PlatformFailure(String)
    } derive(
    Debug
    )

    WindowError

    pub(all) suberror WindowError {
    CreationFailed(String)
    MonitorUnavailable(MonitorId)
    Destroyed
    } derive(
    Debug
    )

    ActiveApp

    pub struct ActiveApp {
    // private fields
    } derive(
    Debug
    )

    ActiveApp::create_window

    fn ActiveApp::create_window(self : ActiveApp, options : WindowOptions) -> Window raise WindowError

    ActiveApp::exit

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

    ActiveApp::exit_with_code

    fn ActiveApp::exit_with_code(self : ActiveApp, code : Int) -> Unit

    ActiveApp::fail

    fn ActiveApp::fail(self : ActiveApp, reason : String) -> Unit

    Requests a checked application failure from a UI callback. The first reason wins. Callers must destroy child runtimes before their parent windows.

    ActiveApp::proxy

    fn ActiveApp::proxy(self : ActiveApp) -> EventLoopProxy

    Returns a sender associated with this running application.

    ActiveApp::set_control_flow

    fn ActiveApp::set_control_flow(self : ActiveApp, control_flow : ControlFlow) -> Unit

    ControlFlow

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

    EventLoop

    pub struct EventLoop {
    // private fields
    } derive(
    Debug
    )

    EventLoop::available_monitors

    fn EventLoop::available_monitors(self : EventLoop) -> Array[Monitor]

    EventLoop::monitor

    fn EventLoop::monitor(self : EventLoop, id : MonitorId) -> Monitor?

    EventLoop::new

    fn EventLoop::new() -> EventLoop raise InitError

    EventLoop::primary_monitor

    fn EventLoop::primary_monitor(self : EventLoop) -> Monitor?

    EventLoop::proxy

    fn EventLoop::proxy(self : EventLoop) -> EventLoopProxy

    Returns a sender that can be moved to a worker thread.

    Only byte messages cross the thread boundary. Their handlers still run on Orby's UI thread through App::proxy_message.

    EventLoop::run_app

    fn[A : App] EventLoop::run_app(self : EventLoop, app : A) -> Int raise AppError

    EventLoop::start_external_app

    fn[A : App] EventLoop::start_external_app(self : EventLoop, app : A) -> ExternalAppLoop[A] raise AppError

    Starts an application without taking ownership of the native event loop.

    Call poll repeatedly from the embedding event loop, then call terminate exactly once after the embedding runtime has stopped. This is intended for integrations such as moonbitlang/async's external event loop support.

    EventLoopProxy

    pub struct EventLoopProxy {
    // private fields
    } derive(
    Debug
    )

    A thread-safe sender for byte messages delivered to App::proxy_message.

    The native queue copies every message, so callers may release the source bytes immediately. It accepts at most 1 MiB per message and 8 MiB in total.

    EventLoopProxy::is_closed

    fn EventLoopProxy::is_closed(self : EventLoopProxy) -> Bool

    Returns whether this event-loop proxy can no longer accept messages.

    EventLoopProxy::post

    fn EventLoopProxy::post(self : EventLoopProxy, message : Bytes) -> Result[Unit, ProxyError]

    Copies a byte message into the native queue and wakes the UI event loop.

    A successful submission is delivered in FIFO order. Delivery stops when the event loop exits; callers must handle Closed as a normal shutdown result.

    ExternalAppLoop

    pub struct ExternalAppLoop[A] {
    // private fields
    }

    A manually pumped application loop for integration with a foreign runtime.

    poll and terminate must run on Orby's UI thread. The callback returned by wakeup_callback_for_foreign_thread is the sole exception: it is safe to invoke from a foreign thread because it calls directly into the native backend without accessing MoonBit-managed application state.

    ExternalAppLoop::poll

    fn[A : App] ExternalAppLoop::poll(self : ExternalAppLoop[A], timeout? : Int) -> ExternalPoll raise AppError

    Pumps pending native events, then waits for no longer than timeout milliseconds. Omitting timeout waits indefinitely. Exited(code) means native cleanup and App::exiting have already run.

    ExternalAppLoop::terminate

    fn[A : App] ExternalAppLoop::terminate(self : ExternalAppLoop[A]) -> Int raise AppError

    Releases native event-loop state and invokes the application's exiting callback. It is idempotent so cleanup paths can safely call it after an initialization or runtime failure.

    ExternalAppLoop::wakeup_callback_for_foreign_thread

    fn[A] ExternalAppLoop::wakeup_callback_for_foreign_thread(self : ExternalAppLoop[A]) -> FuncRef[() -> Unit]

    Returns a wakeup callback suitable for a native foreign thread.

    The callback does not inspect application state and only invokes the native backend wake primitive. Do not wrap it in another MoonBit callback that captures MoonBit-managed values before passing it to a foreign thread.

    ExternalPoll

    pub(all) enum ExternalPoll {
    Continue
    Exited(Int)
    } derive(Eq,
    Debug
    )

    Result of one external event-loop pump.

    InputState

    pub(all) enum InputState {
    Pressed
    Released
    } derive(Eq,
    Debug
    )

    LogicalSize

    pub(all) struct LogicalSize {
    width : Double
    height : Double
    } derive(Eq,
    Debug
    )

    LogicalSize::new

    fn LogicalSize::new(width~ : Double, height~ : Double) -> LogicalSize

    LogicalSize::to_physical

    fn LogicalSize::to_physical(self : LogicalSize, scale_factor : Double) -> Size raise GeometryError

    Modifiers

    pub(all) struct Modifiers {
    shift : Bool
    control : Bool
    alt : Bool
    meta : Bool
    } derive(Eq,
    Debug
    )

    Modifiers::new

    fn Modifiers::new(shift? : Bool, control? : Bool, alt? : Bool, meta? : Bool) -> Modifiers

    Monitor

    pub(all) struct Monitor {
    id : MonitorId
    x : Int
    y : Int
    width : Int
    height : Int
    work_x : Int
    work_y : Int
    work_width : Int
    work_height : Int
    scale_factor : Double
    } derive(Eq,
    Debug
    )

    Monitor::new

    fn Monitor::new(id~ : MonitorId, x~ : Int, y~ : Int, width~ : Int, height~ : Int, work_x~ : Int, work_y~ : Int, work_width~ : Int, work_height~ : Int, scale_factor~ : Double) -> Monitor

    MonitorId

    pub struct MonitorId {
    // private fields
    } derive(Eq,
    Debug
    )

    MonitorId::from_index

    fn MonitorId::from_index(index : Int) -> MonitorId?

    MonitorId::index

    fn MonitorId::index(self : MonitorId) -> Int

    MouseButton

    pub(all) enum MouseButton {
    Left
    Right
    Middle
    Back
    Forward
    Other(Int)
    } derive(Eq,
    Debug
    )

    NativeKeyEvent

    pub(all) struct NativeKeyEvent {
    code : Int
    state : InputState
    repeat : Bool
    modifiers : Modifiers
    } derive(Eq,
    Debug
    )

    NativeKeyEvent::new

    fn NativeKeyEvent::new(code~ : Int, state~ : InputState, repeat? : Bool, modifiers? : Modifiers) -> NativeKeyEvent

    Position

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

    Position::new

    fn Position::new(x~ : Int, y~ : Int) -> Position

    ProxyError

    pub(all) enum ProxyError {
    Closed
    MessageTooLarge
    QueueFull
    } derive(Eq,
    Debug
    )

    A rejected event-loop proxy submission.

    ScrollDelta

    pub(all) struct ScrollDelta {
    x : Double
    y : Double
    } derive(Eq,
    Debug
    )

    ScrollDelta::new

    fn ScrollDelta::new(x~ : Double, y~ : Double) -> ScrollDelta

    Size

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

    Physical client-area size in native pixels.

    Size::new

    fn Size::new(width~ : Int, height~ : Int) -> Size

    Size::to_logical

    fn Size::to_logical(self : Size, scale_factor : Double) -> LogicalSize raise GeometryError

    WebViewHost

    pub struct WebViewHost {
    // private fields
    } derive(
    Debug
    )

    WebViewHost::native_handle

    fn WebViewHost::native_handle(self : WebViewHost) -> UInt64 raise WindowError

    Window

    pub struct Window {
    // private fields
    } derive(
    Debug
    )

    impl Eq for Window

    Window::current_monitor

    fn Window::current_monitor(self : Window) -> Monitor? raise WindowError

    Window::destroy

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

    Window::focus

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

    Requests that this window is shown and activated on the UI thread. Platform foreground-stealing rules may limit activation, but the request always restores a minimized window when the host permits it.

    Window::id

    fn Window::id(self : Window) -> WindowId

    Window::inner_size

    fn Window::inner_size(self : Window) -> Size raise WindowError

    Window::is_destroyed

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

    Window::is_fullscreen

    fn Window::is_fullscreen(self : Window) -> Bool raise WindowError

    Window::is_maximized

    fn Window::is_maximized(self : Window) -> Bool raise WindowError

    Window::is_minimized

    fn Window::is_minimized(self : Window) -> Bool raise WindowError

    Window::request_close

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

    Window::request_redraw

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

    Window::scale_factor

    fn Window::scale_factor(self : Window) -> Double raise WindowError

    Window::set_decorated

    fn Window::set_decorated(self : Window, decorated : Bool) -> Unit

    Window::set_fullscreen

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

    Window::set_fullscreen_on

    fn Window::set_fullscreen_on(self : Window, monitor : MonitorId) -> Unit raise WindowError

    Window::set_inner_size

    fn Window::set_inner_size(self : Window, size : Size) -> Unit

    Window::set_max_inner_size

    fn Window::set_max_inner_size(self : Window, size : Size?) -> Unit

    Window::set_maximized

    fn Window::set_maximized(self : Window, maximized : Bool) -> Unit

    Window::set_min_inner_size

    fn Window::set_min_inner_size(self : Window, size : Size?) -> Unit

    Window::set_minimized

    fn Window::set_minimized(self : Window, minimized : Bool) -> Unit

    Window::set_outer_position

    fn Window::set_outer_position(self : Window, position : Position) -> Unit

    Window::set_resizable

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

    Window::set_title

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

    Window::set_visible

    fn Window::set_visible(self : Window, visible : Bool) -> Unit

    Window::webview_host

    fn Window::webview_host(self : Window) -> WebViewHost raise WindowError

    WindowEvent

    pub(all) enum WindowEvent {
    CloseRequested
    Destroyed
    Resized(Size)
    ScaleFactorChanged(Double)
    FocusChanged(Bool)
    RedrawRequested
    KeyInput(NativeKeyEvent)
    TextInput(String)
    PointerMoved(Position)
    PointerEntered
    PointerLeft
    MouseInput(MouseButton, InputState, Modifiers)
    MouseWheel(ScrollDelta, Modifiers)
    } derive(
    Debug
    )

    WindowId

    pub(all) struct WindowId {
    raw : Int
    } derive(Eq,
    Debug
    )

    WindowId::from_raw

    fn WindowId::from_raw(raw : Int) -> WindowId

    WindowId::raw

    fn WindowId::raw(self : WindowId) -> Int

    WindowOptions

    pub(all) struct WindowOptions {
    title : String
    size : Size
    visible : Bool
    resizable : Bool
    } derive(Eq,
    Debug
    )

    WindowOptions::new

    fn WindowOptions::new(title? : String, size? : Size, visible? : Bool, resizable? : Bool) -> WindowOptions

    validate_scale_factor

    fn validate_scale_factor(scale_factor : Double) -> Bool

    Powered by MoonBit

    Site sourceReport issuePackagesBuild queueSkillsStatistics

    © 2026 mooncakes.io