Writes packed pixels to a Linux framebuffer device (/dev/fbN).
fbdev needs no driver code on the Elixir side: the kernel exposes the display as a file, /sys/class/graphics/fbN describes its geometry, and writing bytes at the right offset lights the pixels.
{:ok, fb} = Framebuffer.open("fb0")
{:ok, format} = Framebuffer.format_for(fb.info)
raster = Raster.new(size: {fb.info.width, fb.info.height}, format: format, scale: 3)
# … on every push:
:ok = Framebuffer.write(fb, patches)Stride
A framebuffer line may be longer than width * bytes_per_pixel (the driver pads it to an alignment). Patches carry no padding, so write/2 places every patch row at y * stride + x * bytes_per_pixel, and a full frame is written row by row when the stride differs from the visible line.
Consoles
The kernel's framebuffer console (fbcon) draws on the same device, so text and a blinking cursor can appear over the app. unbind_console/2 detaches it; moving the system console off the display (a serial ctty on Nerves) keeps the boot log away too.
Options
Every function that touches the filesystem accepts :root (default "/"), prepended to /sys/... and /dev/.... It exists for tests and for devices that mount sysfs elsewhere.
Summary
Functions
The largest integer font scale that keeps at least columns: (default 100) cells of the font (default RasterExRatatui.Font.Default6x8) along the panel's long side, and never less than 1.
Blanks (true) or unblanks (false) the display through /sys/class/graphics/<name>/blank.
Closes the device.
The pixel format for a framebuffer's depth: RGB565 at 16 bits, XRGB8888 at 32.
Reads the geometry of framebuffer name ("fb0") from /sys/class/graphics/<name>: virtual_size, bits_per_pixel, and stride.
Reads the geometry of framebuffer name and opens /dev/<name> for writing.
Detaches the kernel's framebuffer console so it stops drawing text and a cursor over the display.
Writes a list of patches, or a {:frame, binary} covering the whole display, in one :file.pwrite/2 call.
Types
@type info() :: %{ width: pos_integer(), height: pos_integer(), bits_per_pixel: pos_integer(), stride: pos_integer() }
Framebuffer geometry, from sysfs.
@type t() :: %RasterExRatatui.Framebuffer{ device: :file.io_device(), info: info(), name: String.t() }
Functions
@spec auto_scale({pos_integer(), pos_integer()}, keyword()) :: pos_integer()
The largest integer font scale that keeps at least columns: (default 100) cells of the font (default RasterExRatatui.Font.Default6x8) along the panel's long side, and never less than 1.
The long side is the same whichever way the panel is rotated, so this holds for the app's logical image too.
Examples
The Touch Display 2, 720×1280, gets 12×16 pixel cells: 60×80 in portrait, 106×45 turned; a 1080p monitor gets 18×24 pixel cells, 106×45.
iex> RasterExRatatui.Framebuffer.auto_scale({720, 1280})
2
iex> RasterExRatatui.Framebuffer.auto_scale({1920, 1080})
3
iex> RasterExRatatui.Framebuffer.auto_scale({3840, 2160}, columns: 200)
3
iex> RasterExRatatui.Framebuffer.auto_scale({480, 320})
1
Blanks (true) or unblanks (false) the display through /sys/class/graphics/<name>/blank.
Closes the device.
@spec format_for(%{bits_per_pixel: pos_integer()}) :: {:ok, module()} | {:error, :unsupported}
The pixel format for a framebuffer's depth: RGB565 at 16 bits, XRGB8888 at 32.
Examples
iex> RasterExRatatui.Framebuffer.format_for(%{bits_per_pixel: 32})
{:ok, RasterExRatatui.PixelFormat.XRGB8888}
iex> RasterExRatatui.Framebuffer.format_for(%{bits_per_pixel: 16})
{:ok, RasterExRatatui.PixelFormat.RGB565}
iex> RasterExRatatui.Framebuffer.format_for(%{bits_per_pixel: 24})
{:error, :unsupported}
Reads the geometry of framebuffer name ("fb0") from /sys/class/graphics/<name>: virtual_size, bits_per_pixel, and stride.
Returns {:error, reason} when a file is missing or does not hold what fbdev writes there.
Reads the geometry of framebuffer name and opens /dev/<name> for writing.
Detaches the kernel's framebuffer console so it stops drawing text and a cursor over the display.
Writes 0 to /sys/class/vtconsole/<console>/bind. The framebuffer console is usually "vtcon1" ("vtcon0" is the dummy console); /sys/class/vtconsole/*/name tells them apart. Best effort: returns {:error, reason} when the kernel has no such console.
@spec write(t(), [RasterExRatatui.Patch.t()] | {:frame, binary()}) :: :ok | {:error, term()}
Writes a list of patches, or a {:frame, binary} covering the whole display, in one :file.pwrite/2 call.