Display
A display keeps its RGB image and resolution in the world. Stopping a program, rebooting or unloading its computer, disconnecting a network, unloading display chunks and restarting the server preserve the last published image. Programs clear it explicitly. Clicking a display does not send keyboard input.
Place a display next to a computer or bind it to the same peripheral network with the Peripheral Configurator.
Give the screen a name in the configurator’s naming mode. Sides such as front and left are relative to the computer.
import compukter.display.Colors
import compukter.display.DisplayMode
import compukter.display.GraphicalDisplay
import compukter.display.frame
import compukter.display.rgb
fun main() {
val screen = GraphicalDisplay.named("panel")
screen.setMode(DisplayMode.LOW)
screen.frame {
clear(rgb(12, 18, 28))
fillRect(2, 2, width - 4, height - 4, rgb(30, 50, 80))
text(3, 3, "Ready", Colors.WHITE)
pixel(0, 0, Colors.RED)
}
}
Composite screens
Cycle the Peripheral Configurator to Display assembly by using it in the air. Click two display blocks to select opposite corners of a rectangle, at most 8 by 8 blocks. All panels must face the same direction and occupy one plane. The whole rectangle must be loaded while assembling. Empty positions inside it are valid holes, including at creation. Assembly creates a new blank canvas from standalone panels; an existing composite screen must not overlap the selection.
The selected screen remains in the configurator. Click a standalone panel outside its rectangle to expand it, up to 8×8 blocks; the whole expanded rectangle must be loaded. Compatible standalone panels inside the new rectangle join too. The screen keeps its image at the same physical positions, resolution, name and network; newly covered pixels start black. Shift-click a panel to select an existing screen; shift-use in the air to clear the selection. A replacement placed in a hole automatically rejoins when its orientation matches exactly one existing screen. A panel facing another way stays separate until it is reoriented. Overlapping screen holes leave the panel separate; select the intended screen and click the panel to join manually. Last-panel destruction still deletes the screen, so a later replacement starts a new one.
To dismantle a screen, Shift-click to select it, then Shift-click one of its panels again. Each installed panel becomes an independent 1×1 screen, keeping its portion of the image, resolution and network. The shared name and configurator selection are cleared; empty slots no longer reserve an area. All installed panels must be loaded. Expansion and splitting invalidate old program handles and discard unfinished private frames; reopen the display from the program. While the configurator is held in either hand, softly animated outlines show nearby devices, networks and screens. They are hidden by walls. Joined displays have one outer rectangle without borders between their panels in every mode, including Network binding, where the rectangle uses the network color. Hovered and selected groups are brighter; missing panels remain marked by dashed holes. An amber panel inside a screen belongs to a separate screen and has not joined the surrounding assembly, for example because its orientation differs or several screens could claim it. Point at it for the membership and click-action hints. Selecting the first corner previews a valid rectangle while aiming at the second corner. Putting the configurator away removes the overlay.
A composite screen has one network binding, one name, one resolution, one image and one writer lease. Connecting or disconnecting any panel changes the binding of the whole screen. Assembly and joining inherit the only network used by the participating screens; unbound panels do not interfere. If participating screens belong to different networks, assembly or joining succeeds and disconnects the resulting screen from all networks. Reaching any loaded member reaches the screen; typed discovery lists it once. Coordinates start at the top left when viewing its front, increase rightward and downward, and include holes. Breaking a panel preserves geometry and pixels behind the hole. A new panel joined there shows those pixels. Breaking the last panel deletes the canvas, name and resolution. Unloading a chunk does not.
Resolution and drawing
Only a program changes resolution. The last applied mode is saved; changing mode clears the image to black, while
selecting the current mode preserves it. width, height and mode describe the canvas, including its holes.
| Mode | Pixels per block |
|---|---|
ULTRA_LOW |
16 × 16 |
LOW |
32 × 32 |
NORMAL (new screen default) |
64 × 64 |
HIGH |
128 × 128 |
Colors are integers from 0x000000 to 0xFFFFFF, encoded as 0xRRGGBB. rgb(red, green, blue) accepts channels from
0 to 255. Colors includes black, white, red, green, blue, yellow, cyan and magenta. There is no alpha channel.
clear(color = Colors.BLACK)fills the canvas.pixel(x, y, color)andline(x0, y0, x1, y1, color)draw pixels and lines.rect(x, y, width, height, color)draws a border;fillRect(...)fills a rectangle.text(x, y, text, color = Colors.WHITE, scale = 1)draws monochrome JetBrains Mono glyphs in 6 × 12 pixel cells. Scale is 1–8; glyphs are rasterized at the requested size, with cells of6 × scaleby12 × scalepixels, preserving detailed strokes without smoothing. Newline advances one line. Unsupported glyphs use a replacement. Limits are 256 code points and 1024 UTF-8 bytes per call.image(x, y, width, height, pixels)draws row-majorIntArrayRGB pixels. Dimensions are 1–1024 and the image contains at most 65,536 pixels.
Graphics are clipped at canvas boundaries. Rectangle sizes cannot be negative. Drawing publishes immediately;
there is no required present() call. Server work and network transfer are bounded, so large updates can be delayed.
Clients keep the previous image until all loaded panels have received a complete publication.
Optional frames and ownership
Import compukter.display.frame to group drawing with screen.frame { ... }. The changes remain private until the
block returns, then publish together. An exception discards them. Frames cannot nest, and another cooperative task
cannot access that screen while its frame is open. A program may hold at most 262,144 pixels in open frames combined;
choose a lower density for a large screen. A frame also has a cumulative drawing-work limit.
Hibernation preserves a private frame for the suspended program without publishing it. Restarting or terminating that program discards the private frame and leaves the last published image. A normal program restart acquires a new lease and sees the saved resolution.
The first program to draw holds the exclusive writer lease. Other programs cannot overwrite the screen until it
finishes or disconnects. Discovery alone does not claim a lease. GraphicalDisplay supports the standard typed
named, at, first, all and filter queries, with OrNull forms for optional selection. Missing strict queries
throw; ambiguous names are errors. A handle belongs to that logical canvas. Losing every reachable loaded panel makes
it stale; replacing or reconnecting a device does not revive a stale handle.
Geometry types
The reusable types belong to the Guest standard library: import kotlin.math.*. Vector2i describes integer
pixel positions and sizes; Position2i and Size2i are aliases, so either spelling accepts the same value.
Vectors and rectangles also work without a display. The scalar coordinate overloads remain available.
val position = 10 xy 20
val size = 80 by 24
val bounds = position sized size
screen.fillRect(bounds, Colors.BLUE)
screen.rect(position, size, Colors.WHITE)
screen.line(position, position + (20 xy 10), Colors.GREEN)
screen.text(position, "Ready", scale = 2)
val dimensions: Vector2i = screen.size
pixel, line, fillRect, rect, text and image accept vector positions; rectangles can also be passed
to fillRect and rect. image(position, size, pixels) uses the same row-major RGB image contract. Floating
coordinates require explicit roundToInt(), floorToInt() or ceilToInt() conversion before drawing.
TextDisplay.writeAt(position, text) uses text-grid cells, rather than pixels.
A Rect2i has nonnegative size and half-open bounds: its position is included, while right/bottom are excluded.
point in bounds, intersection and bounding union follow those same bounds. Integer rectangle extents must
fit Int. See standard-library mathematics for the vector
types, their aliases, arithmetic, floating normalization/interpolation and rounding semantics.
Existing text programs
TextDisplay remains available with a character grid spanning the whole screen: 20 columns per block across and
10 rows per block down. A 3×2-block screen has 60 columns and 20 rows, including holes:
import compukter.display.TextDisplay
fun main() {
val screen = TextDisplay.named("panel")
screen.clear()
screen.writeAt(0, 0, "Warehouse")
}
writeAt selects HIGH density and rasterizes text into the same persistent RGB canvas. Cells use a continuous
6×12-pixel grid from the canvas’s top-left corner; text can cross panel boundaries. Changing from another density
clears the image first. Each write replaces its cells, including spaces. Text must fit within
one row, contain no control characters or line breaks, and use at most 256 UTF-8 bytes. clear() clears the entire
canvas. Existing text and graphical programs share the same writer lease.