display(
canvas,opts) ->display,err
Create an X11 window display for a canvas
| opts field | default | description |
|---|---|---|
filter | nearest | the look, one of gfx.x11_core.filters: nearest, smooth or scanline |
scale | see below | the integer scale the window opens at |
fullscreen | false | open fullscreen |
title | lilgame | the window's title |
class | lilgame | WM_CLASS, instance and class alike |
display | $DISPLAY | :N or :N.S |
xauthority | $XAUTHORITY, else ~/.Xauthority | the file the cookie is read from |
on_focus | nil | fn(focused), called when the window gains or loses the keyboard |
on_close | nil | fn(), called when the WM asks the window to close |
open | gfx.x11_core.connect | the connector; a suite hands in one that answers a fake handle |
The window opens at the canvas times scale. The default is one step
under the largest scale the screen holds, at least 1, which leaves room
for a WM's panels: 5 on a 2880x1800 screen, 2 on 1920x1080. Fullscreen
opens at the screen's size. The window asks the WM for the canvas's size
as its least.
Returns nil and an error, and opens nothing, when:
scaler is given, or scale is not a
whole number from 1;$DISPLAY is unset or names a host;The display answers the contract gfx.display states, and adds closed,
keyboard and fullscreen. In the window the methods read:
| method | description |
|---|---|
d:update() | read the window's events, wait for the previous frame, send the canvas, compose and present it |
d:focused() | true while the window holds the keyboard |
d:closed() | true once the WM asked the window to close |
d:keyboard() | the window's keys as a device, below |
d:fullscreen(on) | ask the WM for fullscreen, or back |
d:activate(n) | nil and an error: a window is on no VT |
d:destroy() | close the connection; the window goes with it. Safe to call twice |
d:set_filter(name) | switch the look while open, from the next update; true, or nil and an error for an unknown name |
d.cfg.scaler is always server. The window adds fullscreen as asked,
on_close, and pacing: present when the server has Present, loop
when it has not. A resize by the WM lays the canvas out again inside the
next call that reads events. A window smaller than the canvas crops it at
scale 1, centred, and x and y go below 1.
update composes the frame into a window-sized pixmap and hands it to
the window with Present, which copies it at the next vblank, or at the
compositor's next frame under Xwayland and gamescope. The copy is never
a flip, so the next frame never draws into a buffer on the screen. Each
update waits for the previous frame's CompleteNotify before it draws,
so the display paces a frame loop at the refresh. A lost CompleteNotify
costs one frame: the wait times out after a second. Under Xwayland a
window the compositor does not draw, out of sight or in a session paused
for another VT, completes a frame about once a second, and so does the
loop. Without Present the pixmap is copied onto the window at once, and
the frame loop's own deadline paces it.
update also sends a frame of about the canvas's size times four bytes,
more than a Unix socket buffers, and waits writable for the rest. Both
waits yield to LEV inside lev.run, and block in C before it. The
display is the socket's one waiter: LEV holds one per descriptor.
focused starts true and follows the server's focus. A focus change
during a grab is not one: a WM's shortcut grabs the keyboard and lets it
go. on_focus runs on a change alone.
d:keyboard() returns the one keyboard of the display, {read, close}.
read() never waits: it reads the window's events and returns what
queued since the last read, as an evdev device does, {type, code, value} records each followed by a SYN_REPORT, {} when none. The code
is the evdev key code: the X server's keycode less 8, under Xorg's evdev
and libinput drivers and under every Xwayland. A held key repeats with
value 2. Every key down is released when the window loses the keyboard.
A lost connection or an X error answers nil and the error, and update
raises it. Keys queue only while the keyboard is open; close() drops
the queue, and keyboard() opens it again. A read from another task
while update waits on the socket returns what that wait read.