Skip to main content

Display module

Display opens the Windows window or the board's screen and presents on it what you draw. It also keeps the game's pace (fps and dt), the background of each frame and the view, the part of the screen where drawing happens. To draw, use the Draw module, which you have in Display.draw.

JARU Display module screenshot for opening screens, setting orientation, and updating graphics

use Display

Platforms​

WindowsESP32 / ESP32-S3ESP32-P4ESP32-S31
Screen sizeThe one in open(width, height)The board's panel: open does not use its argumentsThe panel's; open(width, height) sets a view of that size in the centerAs on the P4
Colors with double buffer (mode(2))24 bits per pixel8 bits per pixel: 256 colors16 bits per pixel16 bits per pixel
orientationOnly counts before open: 0 and 2 swap width and heightRotates the panel, after openRotates the panel and centers the view againAs on the P4
backlightDoes nothingTurns the light on or offTurns the light on or offTurns the light on or off
Window sizeFrom x1 to x4, with Window Scale———
update()Also looks after the window: closing it ends the program—Presents at the panel's refresh ratePresents at the panel's refresh rate

The size of each panel, the view on the P4 and the S31 and how colors look on each board are in Display, sound, SD and network and in Memory and limits. Window Scale is in the project's Windows configuration.

Functions​

FunctionWhat it doesReturns
open(width, height)Opens the window or the screentrue
close()Closes the screen and frees what was loadedtrue
update()Presents the frame and prepares the next one; with fps, it waitstrue, or the steps that are due
mode() / mode(n)Reads or changes the drawing modeThe mode / true
orientation(n)Rotates the screentrue
backlight(on)Turns the screen's light on or offtrue
loadBG(file)Loads the background image of each frametrue
loadTFT(file) / loadTFT(x, y, file)Draws a fixed image outside the double buffer, like a frametrue

With an argument of another type, the Display functions do not throw an exception: they return an error value and do nothing (see Errors and exceptions). The few that do throw say so in their section.

open​

Opens the window (Windows) or the board's screen.

use Display

Display.open(320, 240)
println(Display.width, " x ", Display.height) // 320 x 240 on Windows
  • width and height are integers. With decimals it does not open: it returns an error value.
  • On Windows, the window measures width × height, or height × width if you have called orientation(0) or orientation(2) before. With Window Scale at x2, x3 or x4, the window looks bigger, but the program still sees width × height.
  • On the ESP32 and the S3, the size is the panel's, given by the board profile: the arguments are required, but they are not used.
  • On the P4 and the S31, the size is also the panel's, and width × height is the view, centered on it (unless you have set viewWidth and viewHeight before).

After open, Display.width and Display.height tell the size of the screen, and viewWidth and viewHeight, the size of the view.

close​

Closes the window or releases the screen. It frees the double buffer, the background image and the loadTFT frame, and sets showBG back to false, autoClear to true and the view to 0.

Display.close()

You can open it again with open afterwards. If the program goes on doing other things, close it to free that memory.

update​

Presents what you have drawn since the previous update().

use Display

Display.open(320, 240)
Display.fps = 30 // the pace: 30 frames per second
var draw = Display.draw

for (var i = 0; i < 90; i++) // 3 seconds
draw.circle(160, 120, 10 + i)
Display.update() // presents, clears the view and waits
end
Display.close()
  • In double buffer mode (the default), update() presents the frame and prepares the next one: it clears it with the background color (if autoClear is on) and draws the background image (if showBG is). Each frame starts clean: there is no need to call draw.cls().
  • In direct mode (mode(1)), what you draw is already on the screen, and update() clears nothing.
  • With a pace (Display.fps = N), update() waits until the next step is due and returns how many steps are due: 1 if the frame fit in its time, 2 or more if it was late (see fps). Without a pace, it returns true.
  • It measures the time between one update() and the next: that is Display.dt, and also the clock that drives the sprites' animations and the physics of scenes.
  • On Windows, update() also looks after the window: if you close it, the program ends there. A program that never calls update() cannot be closed from its window.

With arguments it does nothing and returns an error value.

mode​

Reads or changes the drawing mode.

var currentMode = Display.mode()    // 2, the default
Display.mode(1) // draw straight onto the screen
ModeWhat it does
0Do not use it: it changes with the platform (on Windows, it does the same as 2).
1Direct. Each Draw function draws on the screen at once. update() clears nothing and does not draw the background. What moves flickers.
2Double buffer (the default). You draw on a frame that is not shown, and update() presents it in one go. Then it prepares the next one with autoClear and showBG.

Double buffer is what suits a game. On the ESP32 and the S3 it takes memory (one byte per pixel of the view) and has fewer colors; direct mode does not use that memory. A value outside 0–2 does not change the mode: it returns an error value.

orientation​

Rotates the screen.

Display.orientation(1)    // landscape
ValueRotationOn Windows
00 degreesPortrait: the window measures height × width
190 degreesLandscape (the default)
2180 degreesPortrait
3270 degreesLandscape
  • Only the two lowest bits count: 4 is 0, 5 is 1…
  • On Windows, it only counts before open, to choose a portrait or a landscape window. It does not rotate what you draw.
  • On a board, it rotates the panel, and Display.width and Display.height change with it. Call it after open: before, it does nothing, and the screen starts with the rotation given by the board profile. On the P4 and the S31 it also centers the view again.

backlight​

Turns the screen's light on or off.

Display.backlight(false)  // turns the light off
Display.backlight(true) // turns it on

It takes true, false or a number: 0 turns it off, and any other number turns it on. On a board it does not set the brightness: the light is at full or off. On Windows it does nothing. It depends on the board profile saying which pin controls the light.

loadBG​

Loads a BMP image as the background. With showBG set to true, update() draws it when it starts each frame, at the posBG position of the view.

Display.loadBG("Images/background.bmp")
Display.showBG = true // without this, the background is not drawn
Display.posBG = [0, 0]
  • The path is the project's, as in Bitmap.load: the image has to reach the game (Include in Build in the image editor). It reads the same formats as Bitmap.
  • It is only drawn in double buffer mode.
  • If it cannot find the image, it returns false, writes the error to the console (on a board, to the serial monitor) and leaves the previous background as it was.

loadTFT​

Draws a BMP image straight onto the screen, outside the double buffer. It is meant for a frame or a fixed decoration around the view: since update() only redraws the view, what is outside it stays.

// A frame at (0, 0)
Display.loadTFT("Images/frame.bmp")

// An image at a position
Display.loadTFT(50, 100, "Images/logo.bmp")
  • x and y are integers: the top left corner, in screen coordinates (not view coordinates). Without them, the image goes at (0, 0).
  • With arguments that are not valid, it throws ERR_TYPE_MISMATCH or ERR_INVALID_ARG_NUMBER. If it cannot load the image, on a board it throws ERR_FILE_READ_FAIL, and on Windows it returns an error value.
On Windows, the image fills the whole window

On Windows, loadTFT does not use x or y: it stretches the image over the whole window. To make it look the same as on the board, make the frame the size of the screen.

Properties​

PropertyTypeDefaultRead / write
width, heightinteger320 and 240 until open; then, the screen sizeRead only
viewWidth, viewHeightinteger0 until open; then, the view sizeBoth (assign them before open)
fpsinteger, from 0 to 10000: no paceBoth
dtdecimal, in seconds0Read only
showBGyes/nofalseBoth
autoClearyes/notrueBoth
posBGlist [x, y][0, 0]Write only
drawthe Draw module—Read only

Properties are assigned with =: Display.fps = 30. Calling them like a function (Display.fps(30)) throws ERR_INVALID_OPERATION, and assigning width, height or draw throws ERR_ATTRIBUTE_NOT_FOUND.

posBG​

The position of the background image inside the view, as a list [x, y]. Decimals are truncated.

Display.posBG = [10, 20]

It is write only: reading Display.posBG does not return the position, but an error value.

showBG​

If it is true, update() draws the loadBG image when it prepares each frame. It is false by default.

Display.showBG = true

autoClear​

If it is true, update() clears each new frame with the background color, draw.colorBG. It is true by default.

Display.autoClear = false   // what you draw stays from one frame to the next

With false, what you have drawn is still there in the next frame: it suits a program that only redraws what changes. showBG and autoClear only count in double buffer mode (2); in direct mode, update() does not draw any background.

With autoClear and showBG, update() clears first and then draws the background image: what the image does not cover keeps the background color.

viewWidth​

The width of the view: the part of the screen where Draw draws. Draw coordinates start at the view's top left corner, and what falls outside it is not shown. The view is centered on the screen.

use Display

// A 240 x 160 view in a 320 x 240 window
Display.viewWidth = 240
Display.viewHeight = 160
Display.open(320, 240)
println(Display.viewWidth) // 240
  • Assign it before open. Until then it is 0, and open sets it to the screen size if you have not given it another one.
  • On Windows, the ESP32 and the S3, assigning it after open changes what you read, but not the view. On the P4 and the S31 the view does change, and it is centered again.
  • It is meant for leaving a frame around the game (with loadTFT), or for making a game made for a small screen look the same on a big one.
  • On Windows, some Draw functions do not count from the view: see the Draw pitfalls.

viewHeight​

The height of the view. It works like viewWidth.

Display.viewHeight = 160

fps​

The fps property sets the game's pace: how many logic steps per second it runs, whatever the board and however long each frame takes. It is an integer from 0 to 1000; with 0 (the default) there is no pace and Display.update() never waits.

With Display.fps = N, each Display.update() does not return until the next step is due (1/N of a second after the previous one) and returns how many steps are due. A game that moves a fixed amount per frame no longer speeds up when the board draws faster (for example, when there is no scrolling).

Display.fps = 30          // 30 steps per second on every board

If the board cannot draw N frames per second, a game that only sets Display.fps runs slower during that time. To keep its speed, it can run the missing steps without drawing, using the value update() returns:

Display.fps = 30
var steps = 1
while (true)
while (steps > 1) // the missing steps, without drawing
Logic()
steps = steps - 1
end
Logic()
Render()
steps = Display.update() // 1, or 2 or more if the frame was late
end
  • A value outside 0–1000 stays at the limit, and decimals are truncated. With something that is not a number, it throws ERR_TYPE_MISMATCH.
Long delays

More than 150 ms late (a load, a pause), there is no catch-up: the count starts again at that moment, so the game does not race ahead. Assigning fps also restarts the count.

ESP32-P4 and ESP32-S31

These boards present each frame at their panel's refresh rate. The game still runs at the fps speed, but motion is only even when fps divides the panel refresh rate (on a 60 Hz panel, 30 or 60).

dt​

The dt property (read only) gives the seconds elapsed between the previous Display.update() and the last one, measured by the VM in microseconds (after the fps wait, if any). It is meant for games that move by multiplying by time, instead of computing it with clock().

var dt = Display.dt
ship.x = ship.x + speed * dt

It is 0 until the second update(), and after a load or a pause it is at most 0.15. With Display.fps = N and the frame on time, it is about 1/N: at 30 fps, around 0.0333. Assigning it throws ERR_INVALID_OPERATION.

width​

The screen width in pixels (read only).

var w = Display.width
println("Screen width: ", w)

It is 320 until open. Then, the width of the window or of the panel, and on a board it changes with orientation.

height​

The screen height in pixels (read only).

var h = Display.height
println("Screen height: ", h)

It is 240 until open. Then, the height of the window or of the panel.

draw​

The Draw module, which draws.

var draw = Display.draw
draw.color = 0xFF0000
draw.circle(100, 100, 50, true)

It is the same module you get with use Draw.

Complete example​

A square crosses a 240 × 160 view centered in the window, at 80 pixels per second, the same on every board.

use Display

// A 240 x 160 view in the center of a 320 x 240 window
Display.viewWidth = 240
Display.viewHeight = 160
Display.open(320, 240)
Display.fps = 30 // 30 steps per second, on Windows and on the boards

var draw = Display.draw
draw.colorBG = 0x102040 // the color update() clears the view with

var x = 0.0
var speed = 80 // pixels per second
for (var i = 0; i < 300; i++) // 10 seconds
draw.color = 0xFFCC00
draw.rect(x, 70, 20, 20, true)
Display.update() // presents, clears the view and waits for the next step
x = x + speed * Display.dt
if (x > Display.viewWidth) then
x = -20.0
end
end
Display.close()

Pitfalls and known issues​

  • On Windows, the view does not clip until the first update(). What you draw outside the view before the first update() shows.
  • On a board, open may not open the screen. If the board profile does not describe its screen correctly, open writes “Display: Invalid panel/bus configuration from boot.cfg.” to the serial monitor and returns an error value. Check the profile: Board profiles.
  • On Windows, draw.line, draw.circle, draw.polygon and draw.getPixel do not count from the view. With a view smaller than the screen, they come out shifted. See Draw.
  • On Windows, loadTFT stretches the image over the whole window (see loadTFT).

All the known issues of JARU, together, are in Known issues.