Scenic v0.10.2 Scenic.ViewPort View Source


The job of the ViewPort is to coordinate the flow of information between the scenes and the drivers. Scene's and Drivers should not know anything about each other. An app should work identically from its point of view no matter if there is one, multiple, or no drivers currently running.

Drivers are all about rendering output and collecting input from a single source. Usually hardware, but can also be the network or files. Drivers only care about graphs and should not need to know anything about the logic or state encapsulated in Scenes.

The goal is to isolate app data & logic from render data & logic. The ViewPort is the choke point between them that makes sense of the flow of information.


Practically speaking, the ViewPort is the owner of the ETS tables that carry the graphs (and any other necessary support info). If the VP crashes, then all that information needs to be rebuilt. The VP monitors all running scenes and does the appropriate cleanup when any of them goes DOWN.

The scene is responsible for generating graphs and writing them to the graph ETS table. (Also tried casting the graph to the ViewPort so that the table could be non-public, but that had issues)

The drivers should only read from the graph tables.


When user input happens, the drivers send it to the ViewPort. Input that does not depend on screen position (key presses, audio window events, etc.) Are sent to the root scene unless some other scene has captured that type of input (see captured input) below.

If the input event does depend on position (cursor position, cursor button presses, scrolling, etc.) then the ViewPort needs to scan the hierarchical graph of graphs, to find the correct scene, and the item in that scene that was "hit". The ViewPort then sends the event to that scene, with the position projected into the scene's local coordinate space (via the built-up stack of matrix transformations)


A scene can request to "capture" all input events of a certain type. This means that all events of that type are sent to a certain scene process regardless of position or root. In this way, a text input scene nested deep in the tree can capture key presses. Or a button can capture cursor_pos events after it has been pressed.

If a scene has "captured" a position dependent input type, that position is projected into the scene's coordinate space before sending the event. Note that instead of walking the graph of graphs, the transforms provided in the input "context" field are used. You could, in theory change that to something else before capturing, but I wouldn't really recommend it.

Any scene can cancel the current capture. This would probably leave the scene that thinks it has "captured" the input in a weird state, so I wouldn't recommend it.

Link to this section Summary


Capture one or more types of input

Cast a message to all active drivers listening to a viewport

query the last recorded viewport status

Send raw input to a viewport

Request that a {:set_root, ...} message is sent to the caller

Reset a running viewport

Set the root scene/graph of the ViewPort

Start a new viewport

Stop a running viewport

Link to this section Types

Link to this type

event() View Source
event() :: {event :: atom(), data :: any()}

Link to this section Functions

Link to this function

capture_input(context, input_types) View Source
  context :: Scenic.ViewPort.Context.t(),
  input_class :: Scenic.ViewPort.Input.class() | [Scenic.ViewPort.Input.class()]
) :: :ok

Capture one or more types of input.

This must be called by a Scene process.

Link to this function

driver_cast(viewport, msg) View Source

Cast a message to all active drivers listening to a viewport.

Link to this function

info(viewport) View Source
info(viewport :: GenServer.server()) :: {:ok, Scenic.ViewPort.Status.t()}

query the last recorded viewport status

Link to this function

input(viewport, input_event) View Source
input(viewport :: GenServer.server(), input :: Scenic.ViewPort.Input.t()) :: :ok

Send raw input to a viewport.

This is used primarily by drivers to send raw user input to the viewport. Having said that, nothing stops a scene from using it to send input into the system. There are a few cases where that is useful.

See the input docs for the input formats you can send.

Link to this function

input(viewport, input_event, context) View Source
  viewport :: GenServer.server(),
  input :: Scenic.ViewPort.Input.t(),
  context :: Scenic.ViewPort.Context.t()
) :: :ok

Link to this function

release_input(context_or_viewport, input_types) View Source

release an input capture.

This is intended be called by a Scene process, but doesn't need to be.

Link to this function

request_root(viewport, send_to \\ nil) View Source
  viewport :: GenServer.server(),
  send_to :: nil | GenServer.server()
) :: :ok

Request that a {:set_root, ...} message is sent to the caller.

request_root is primarily used by drivers and is of little use to anything else.

When a driver starts up, it will need to get the root scene of the viewport, which may already be up and running. By calling request_root, the driver can request that the viewport send it a {:set_root, ...} message as if the root scene had just changed.


  • viewport The viewport to request the message from.
  • send_to The driver to send the message to. If send_to is nil, the message will be sent to the calling process.
Link to this function

reset(viewport) View Source
reset(viewport :: GenServer.server()) :: :ok

Reset a running viewport

This causes the viewport to rest the original scene as the root, with the original arguments were received with the ViewPort started.

Link to this function

reshape(viewport, size) View Source
reshape(viewport :: GenServer.server(), size :: Scenic.Math.point()) :: :ok

Link to this function

set_root(viewport, scene, args \\ nil) View Source
  viewport :: GenServer.server(),
  scene :: atom() | {atom(), any()},
  args :: any()
) :: :ok

Set the root scene/graph of the ViewPort.

Link to this function

start(config) View Source
start(config :: map()) :: {:ok, pid()}

Start a new viewport

Link to this function

stop(viewport) View Source
stop(viewport :: GenServer.server()) :: :ok

Stop a running viewport