FmrbApp¶
Note
Under construction.
FmrbApp is the application base class for Family mruby. User apps must inherit from FmrbApp and implement lifecycle methods.
Minimal Example¶
class MyApp < FmrbApp
def on_create
clear_user_area(FmrbGfx::WHITE)
@gfx.draw_text(@user_area_x0 + 4, @user_area_y0 + 4,
"Hello, mruby!", FmrbGfx::BLACK)
draw_window_frame
@gfx.present
end
def on_update
100 # Wait 100ms
end
end
MyApp.new.start
Window size and other settings are specified in a .toml file (see App Configuration File (.toml)).
Lifecycle¶
| Method | When Called | Return Value Meaning |
|---|---|---|
on_create |
Once when the app starts | Any (ignored) |
on_update |
Repeatedly within the main loop | Wait time in milliseconds until the next on_update. Default 330ms |
on_event(ev) |
On keyboard / mouse / gamepad / HID input | Any |
on_suspend |
When switching to a fullscreen app | Any |
on_resume |
When returning from a suspended state | Any |
on_destroy |
Once when the app exits | Any |
start
+-- on_create
+-- main_loop:
+-- on_update -> wait for return value ms via _spin
+-- _spin dispatches on_event(ev), _handle_system_control(msg)
+-- repeats until @running becomes false
+-- destroy -> on_destroy
on_update return value
A short value (10-30ms) increases the frame rate but consumes more CPU. For games, 16-33ms is a good target; for static UIs, 100-500ms is appropriate.
Event Handling (on_event(ev))¶
ev is a Hash, and you determine the event type with ev[:type].
Keyboard¶
def on_event(ev)
case ev[:type]
when :key_down
keycode = ev[:keycode] # Character code (platform-dependent)
scancode = ev[:scancode] # USB HID Usage ID (platform-independent)
modifier = ev[:modifier] # Modifier key bits (see below)
char = ev[:character] # Character (if available)
Log.info("key down: #{char.inspect}")
when :key_up
# ...
end
end
Modifier key bits (ev[:modifier]) layout:
| Bit | Value | Meaning |
|---|---|---|
| 0 | 0x01 | LSHIFT |
| 1 | 0x02 | RSHIFT |
| 2 | 0x04 | LCTRL |
| 3 | 0x08 | RCTRL |
| 4 | 0x10 | LALT |
| 5 | 0x20 | RALT |
Helper methods are provided:
ev_ctrl?(ev) # Ctrl is pressed
ev_shift?(ev) # Shift is pressed
ev_alt?(ev) # Alt is pressed
Note
Use scancode when identifying character keys. keycode values vary between platforms (e.g. SDL2 returns ASCII values).
FmrbConst::KEY_* / MOD_* constants
Since scancode values are USB HID Usage IDs, you can use constants like FmrbConst::KEY_ESC instead of writing raw values like 0x29 (ESC). Modifier key mask constants such as FmrbConst::MOD_CTRL are also available. See Constants > KEY_ / MOD_ for a full list.
Mouse¶
when :mouse_down, :mouse_up
ev[:button] # 1=left, 2=middle, 3=right
ev[:x] # X coordinate within the window
ev[:y] # Y coordinate within the window
when :mouse_move
ev[:x], ev[:y]
Clicks on the title bar (left-click to close / right-click to reload) are already handled by the base class, so these actions work even if the subclass does not call super.
Gamepad¶
when :gamepad_down, :gamepad_up
ev[:gamepad_id] # 0 and above
ev[:button] # 0..15
when :gamepad_axis
ev[:gamepad_id]
ev[:axis] # 0..5
ev[:value] # Axis value
FmrbConst::GP_* constants
Button numbers have constants like FmrbConst::GP_SQUARE / GP_CROSS / GP_START, and axis numbers have GP_AXIS_LX / GP_AXIS_LY, etc. See Constants > GP_* for details.
Window Operations¶
| Method | Purpose |
|---|---|
set_window_position(x, y) |
Change the window position |
draw_window_frame |
Draw the window frame (title bar + border). Reuses a GfxBlock managed by the base class |
clear_user_area(color = FmrbGfx::BLACK) |
Fill the drawable area (excluding title bar and border) with the specified color |
draw_scrollbar(scroll, total, visible, x=..., y=..., w=..., h=...) |
Draw a scrollbar |
scrollbar_hit(click_x, click_y, x=..., y=..., w=..., h=...) |
Scrollbar hit detection (returns :up / :down / nil) |
request_file_select(mode = "open") |
Invoke the system file selection dialog |
request_reload |
Reload the script (automatically called on title bar right-click) |
Use clear_user_area instead of @gfx.clear
@gfx.clear(color) fills the entire canvas, which also erases the title bar and close button. To preserve the window frame, use clear_user_area(color) instead.
Messaging¶
| Method | Purpose |
|---|---|
subscribe(topic) / unsubscribe(topic) |
Subscribe to a topic |
publish(topic, data=nil) |
Send to a topic |
send_message(dest_pid, msg_type, data) |
Direct message to the kernel or a specific app. data is automatically serialized via MessagePack |
For details and receive handlers, see Pub/Sub.
Execution Control¶
| Method | Purpose |
|---|---|
start |
Sets @running = true and starts the event loop (on_create is called) |
stop |
Sets @running = false (proceeds to destroy after the next _spin) |
destroy |
Notifies the kernel of exit, calls @gfx.destroy, on_destroy, _cleanup |
Normally, writing just MyApp.new.start is sufficient.
Key Instance Variables¶
| Variable | Description |
|---|---|
@gfx |
FmrbGfx instance (drawing API; nil in headless mode) |
@audio |
FmrbAudio instance |
@name |
App display name (from .toml app_screen_name) |
@platform |
:esp32 or :linux |
@fullscreen |
true if in fullscreen mode |
@window_width / @window_height |
Overall window size |
@pos_x / @pos_y |
Absolute coordinates of the window's top-left corner |
@user_area_x0 / @user_area_y0 / @user_area_x1 / @user_area_y1 |
Boundaries of the drawable area, excluding title bar and borders |
@user_area_width / @user_area_height |
Size of the drawable area |
@running |
true while the app is running |
@suspended |
true while suspended |
Drawing within the window frame
In windowed mode with a title bar, always draw within the @user_area_* bounds. Start from @user_area_x0, @user_area_y0 and stay within @user_area_width and @user_area_height.
File and Directory Paths¶
Pass root-relative paths (e.g. /data/foo.txt) or SD card paths like /mnt/sd/... directly to File.open / Dir.open. See File & I/O > File Namespace for details.
Class Methods¶
| Method | Purpose |
|---|---|
FmrbApp.ps |
Array of Hash showing all process states (id, name, state, vm_type, mem_*, stack_water, etc.) |
FmrbApp.config(section) |
Read a specified section from the app's .toml |
FmrbApp.wallclock |
Current time ({year, month, day, hour, minute, second}) |
FmrbApp.set_wallclock(year, month, day, hour, minute, second) |
Set RTC / system time |
FmrbApp.gfx_stats |
Drawing statistics {cmds:, presents:} |
FmrbApp.sys_pool_info |
System memory pool information |
FmrbApp.heap_info |
ESP-IDF heap info (free, total, min_free, largest_block, etc.) |
FmrbApp.enable_cursor |
Show mouse cursor (delayed until the first mouse movement) |
FmrbApp.set_cursor_visible(visible) |
Immediately show/hide cursor. Useful for hiding in fullscreen games and restoring on exit |
FmrbApp._get_last_error |
Last app error (returns {name:, error:} if present) |
Constants¶
| Constant | Value | Purpose |
|---|---|---|
TITLE_BAR_H |
11 | Title bar height (px) |
CORNER_R |
4 | Window corner radius |
TRANSPARENT_COLOR |
0x01 | Transparent color (passes through during compositing) |
SCROLLBAR_W |
10 | Scrollbar width |
SCROLLBAR_BTN_H |
10 | Scrollbar button height |
Example: Increment a Counter on Button Press¶
class CounterApp < FmrbApp
def on_create
@count = 0
redraw
end
def on_event(ev)
super # Inherit close button handling
if ev[:type] == :mouse_down && ev[:button] == 1
@count += 1
redraw
elsif ev[:type] == :key_down && ev[:character] == "r"
@count = 0
redraw
end
end
def on_update
300
end
private
def redraw
clear_user_area(FmrbGfx::WHITE)
@gfx.draw_text(@user_area_x0 + 4, @user_area_y0 + 4,
"Count: #{@count}", FmrbGfx::BLACK)
draw_window_frame
@gfx.present
end
end
CounterApp.new.start