MingaEditor.Frontend.Manager (Minga v0.1.0)

Copy Markdown View Source

GenServer that manages the frontend renderer Port.

Operates in two modes depending on the MINGA_PORT_MODE env var:

  • Spawn mode (default): BEAM is the parent process. Port.Manager spawns the GUI/TUI binary as a child via Port.open({:spawn_executable, ...}). Used in development, TUI mode, and Burrito releases.

  • Connected mode (MINGA_PORT_MODE=connected): BEAM is a child of the GUI process. The GUI set up stdin/stdout pipes before launching us. Port.Manager opens {:fd, 0, 1} as a Port instead of spawning a child. Used when launching from Minga.app (Finder, Spotlight, Dock).

Both modes use identical {:packet, 4} framing. The protocol layer (event decoding, render commands, subscriber broadcasting) is the same.

Output admission always uses Port.command/3 with :nosuspend. If the transport is unwritable, the manager retains one current frame and one latest coalesced replacement, retries within a short budget, then requests correlated keyframe recovery. Synchronous admission calls keep frame binaries out of the manager mailbox while leaving the process free to receive frontend input between attempts.

Subscribers register via subscribe/1 and receive messages as:

{:minga_input, event}

where event is a MingaEditor.Frontend.Protocol.input_event().

Summary

Types

Non-suspending frontend transport admission result.

Renderer backend.

Options for starting the port manager.

Internal state.

Functions

Returns the frontend's reported capabilities.

Returns a specification to start this module under a supervisor.

Returns bounded output-pressure diagnostics.

Returns whether the renderer has sent its ready signal.

Attempts non-suspending admission of encoded commands to the renderer.

Like send_commands/2 but stamps the synchronous admission request with a monotonic send time. The receiver emits a [:minga, :render, :hop_latency] (hop: :send_commands) sample measuring the Renderer.Server to Port.Manager scheduling delay. Used only for the per-frame render batch on the keystroke path.

Starts the port manager.

Subscribes the calling process to receive input events.

Returns the terminal size as {width, height}, or nil if not yet ready.

Builds a /dev/ path from the tty name returned by ps -o tty=.

Types

admission()

@type admission() :: :accepted | :unwritable

Non-suspending frontend transport admission result.

backend()

@type backend() :: :tui | :gui

Renderer backend.

start_opt()

@type start_opt() ::
  {:name, GenServer.name()}
  | {:renderer_path, String.t()}
  | {:backend, backend()}
  | {:port_mode, MingaEditor.Frontend.Manager.State.port_mode()}
  | {:port_opener, (term(), [term()] -> port())}
  | {:port_commander, MingaEditor.Frontend.Manager.State.port_commander()}
  | {:output_retry_ms, pos_integer()}
  | {:output_failure_ms, non_neg_integer()}
  | {:tty_path, String.t() | nil}

Options for starting the port manager.

state()

Internal state.

Functions

capabilities(server \\ __MODULE__)

Returns the frontend's reported capabilities.

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

output_pressure(server \\ __MODULE__)

Returns bounded output-pressure diagnostics.

ready?(server \\ __MODULE__)

@spec ready?(GenServer.server()) :: boolean()

Returns whether the renderer has sent its ready signal.

send_commands(server \\ __MODULE__, commands)

@spec send_commands(GenServer.server() | nil, [binary()]) :: admission()

Attempts non-suspending admission of encoded commands to the renderer.

send_render_commands(server \\ __MODULE__, commands)

@spec send_render_commands(GenServer.server() | nil, [binary()]) :: admission()

Like send_commands/2 but stamps the synchronous admission request with a monotonic send time. The receiver emits a [:minga, :render, :hop_latency] (hop: :send_commands) sample measuring the Renderer.Server to Port.Manager scheduling delay. Used only for the per-frame render batch on the keystroke path.

start_link(opts \\ [])

@spec start_link([start_opt()]) :: GenServer.on_start()

Starts the port manager.

subscribe(server \\ __MODULE__)

@spec subscribe(GenServer.server()) :: :ok

Subscribes the calling process to receive input events.

terminal_size(server \\ __MODULE__)

@spec terminal_size(GenServer.server()) :: {pos_integer(), pos_integer()} | nil

Returns the terminal size as {width, height}, or nil if not yet ready.

tty_path_for(tty_name)

@spec tty_path_for(String.t()) :: String.t() | nil

Builds a /dev/ path from the tty name returned by ps -o tty=.

The format varies by OS and version:

  • macOS long form: "ttys008""/dev/ttys008"
  • macOS short form: "s003""/dev/ttys003"
  • Linux: "pts/3""/dev/pts/3"

Checks if /dev/{name} exists first (handles long form and Linux). Falls back to /dev/tty{name} for short forms.

Returns nil when the process has no controlling terminal. ps -o tty= reports this as all question marks: "??" on macOS, "?" on Linux. Without this guard we would build a bogus path like /dev/tty?, which the Go renderer cannot open and crashes on. Returning nil lets the renderer fall back to /dev/tty.