display(
canvas,opts) ->display,err
Create a DRM console display for a canvas
| opts field | default | description |
|---|---|---|
path | /dev/dri/card0 | DRM card node |
manage_console | true | hold the console, see below |
on_focus | nil | fn(focused), called when a VT switch takes the display away or brings it back |
open | gfx.drm_core.open | the card opener; a suite hands in one that opens a fake card |
filter | nearest | the look, one of gfx.drm_core.filters: nearest, smooth or scanline |
scaler | nil | "cpu" keeps the scaling off the plane, for a comparison by eye |
Returns nil and an error, and leaves the console alone, when:
scaler is a word other than "cpu";The display answers the contract gfx.display states, and adds nothing
to it. On the console the methods read:
| method | description |
|---|---|
d:update() | answer a VT switch; wait for the pending flip, blit the canvas, flip |
d:focused() | true while the display's VT is in view and the card is its own |
d:activate(n) | switch to VT n; nil and an error off a VT |
d:destroy() | restore the CRTC and the console, close the card; safe to call twice |
d:set_filter(name) | switch the look while open; true, or nil and an error |
d.cfg.scaler is plane when the display engine scales, cpu when the
packer does. nearest and smooth ask for the plane; scanline never does. A
card that refuses the plane, no scaler, or no nearest-neighbour filter
past scale 1, gets the packer: nearest as itself, smooth as
nearest, and cfg says so, since a game runs on any card rather than
refuse a look. The refusal's reason is d.__state.scaler_err. At scale 1
every filter draws the same.
set_filter leaves the display where an open with that filter would have
put it on this card, and cfg reports the look in effect after it as
after the open. A display opened with scaler = "cpu" stays on the
packer. The look last asked for asks the card nothing, so a caller may
apply its settings every frame. Between nearest and smooth on the
plane only the plane's filter changes, and the frame on the screen stays.
A move between the plane and the packer shows one black frame. The call
answers nil and an error, the look unchanged, for an unknown name, while
another VT is in view, and when the card refuses to take the plane down.
A change on the plane commits blocking, after the flip in flight lands,
and that drains the flip's event: call it from the task that runs
update, or that update's wait sleeps out its second.
update waits for the previous flip before it blits, so the display paces
a frame loop at the panel's refresh. The wait yields to LEV, so every
update after the first must run inside lev.run. The first has nothing
to wait for and runs anywhere. A lost flip event costs one frame: the wait
times out after a second, and the flip after it is refused as pending. A
third flip refused in a row, for any reason the card gives, raises from
update with the card's words; a flip that lands starts the count again.
With manage_console, the display puts the controlling tty in raw mode,
so keys neither echo nor raise signals, and hides the cursor. At a VT it
also puts the VT in graphics mode, so the kernel's console draws nothing
over the frame, and turns the VT's keyboard off, so keys reach evdev
alone and nothing queues on the tty. The kernel's own console-switch chord
goes with the keyboard: a program binds a chord and calls activate. A tty
that is not a VT, a terminal or an ssh session, is held in raw mode alone.
Off a tty nothing is held and no escape is written.
A switch to another VT is answered inside update: the display hands the
card to the console and stops drawing, and on_focus(false) runs. A switch
back takes the card again and on_focus(true) runs. The kernel holds a
switch until it is answered, so keep calling update while another VT is
in view. gfx.loop does, unless paused. While away, update draws
nothing.
The card handle keeps the console's state. Closing it puts everything back,
from destroy or from the garbage collector, so a display dropped by an
error still returns the console. A fatal signal at a VT puts the console
back too, then ends the process as it would have.
The display reads no input. Read keys from evdev.