Install Requirements
In addition to everything covered on theĀ Raspberry Pi 5 Setup page, these examples require a few additional requirements to be installed: Xvfb and PyVirtualDisplay. Install them with the following commands.
sudo apt install xvfb source ~/venvs/blinka_venv/bin/activate pip install pyvirtualdisplay
Run Virtual Display Example
This example provides a mirrored display only. This works well if the app that you are mirroring has its own built-in support for handling USB gamepads or other inputs. Some game console emulators have support for controllers built-in, any that do can be used with this script. If your app does not have its own input support, you can use the example from the Mirror X Display page for a different method that allows the keyboard and mouse to work, but it requires different setup and configuration from this one.
As always, you need to activate the virtual environment before running the example. If you haven't already, then activate it like this.
source ~/venvs/blinka_venv/bin/activate
Next you can run the virtualdisplay.py script. The script accepts several arguments to configure the panels. The first 5 are specific to the virtualdisplay example.
-
--scale
- The scale factor, larger numbers mean more virtual pixels. Default is 1. -
--backend
- The pyvirtualdisplay backend to use. Supported values are xvfb, and xvnc, default is xvfb. -
--extra-args
- Extra arguments to pass to the backend server. -
--rfbport
- The port number for the--backend xvnc
. -
--use-xauth/--no-use-xauth
- If a Xauthority file should be created. Default is False.
The remaining arguments are of the configuration options described on the Initialization Config page. The first two are required.
-
--width
- the number of pixels wide the total panel is. 128px is for a 2x2 matrix panel. -
--height
- the number of pixels tall the total panel is. 64px is for a 2x2 matrix panel.
The rest are optional if you're using default the configuration for everything.
-
--orientation
- the rotation angle, must be one ofNormal
,CW
,ĀCCW
,R180
.Ā -
--pinout
- the pinout to use for the panel(s) e.g.AdafruitMatrixBonnet
-
--num-address-lines
- the number of address lines. The 64x64 panels we stock useĀ5
, all smaller panels use4
-
--num-planes
- the color depth, defaults to10
. Lower values can improve refresh rate speed. -
n_temporal_planes
- Controls the temporal dithering. Valid values are0
,2
, and4
. Cannot be higher thann_planes
. The default is0
. Using larger values can increase the framerate in some cases experiment with your panels and application. -
--serpentine
- the organization of multiple panels, serpentine is default. Use--no-serpentine
if your panels are wired in a non-serpentine configuration.
Mirrored App Command
After all of the other arguments, at the end of the command you will include two dashes surrounded by spacesĀ Ā --
, following those you put what ever command is needed to launch the app that you want to mirror including any arguments that it requires.Ā
The command should look as follows with the appropriate values filled in for your panel:
python virtualdisplay.py --scale [scale] --width [width]
--height [height] --orientation [orientation] --num-address-lines
[addrlines] --num-planes [planes] -- [command launching app to mirror]
The values for a 2x2 matrix panel made from 64x64 panels rotated 180 are shown below, adjust them for your panel(s).
python virtualdisplay.py --pinout AdafruitMatrixHatBGR --scale 2 --backend xvfb --width 128 --height 128 --serpentine --num-address-lines 5 --num-planes 4 -- mednafen -snes.xscalefs 1 -snes.yscalefs 1 -snes.xres 128 -video.fs 1 -video.driver softfb /tmp/snesrom.smc
This command is launching Mednafen, a multi-system emulator application. If you're launching something different then replace everything after the double hyphen with your own launch command.
The code forĀ virtualdisplay.py is embedded below. You can download it, or copy/paste the text into a file.
#!/usr/bin/python3 """ Display a (possibly scaled) X session to a matrix The display runs until the graphical program exits. The display doesn't get a keyboard or mouse, so you have to use a program that will get its input in some other way, such as from a gamepad. For help with commandline arguments, run `python virtualdisplay.py --help` This needs additional software to be installed (besides a graphical program to run). At a minimum you have to install a virtual display server program (xvfb) and the pyvirtualdisplay importable Python module: $ sudo apt install -y xvfb $ pip install pyvirtualdisplay Here's an example for running an emulator using a rom stored in "/tmp/snesrom.smc" on a virtual 128x128 panel made from 4 64x64 panels: $ python virtualdisplay.py --pinout AdafruitMatrixHatBGR --scale 2 --backend xvfb --width 128 --height 128 --serpentine --num-address-lines 5 --num-planes 4 -- mednafen -snes.xscalefs 1 -snes.yscalefs 1 -snes.xres 128 -video.fs 1 -video.driver softfb /tmp/snesrom.smc """ import shlex from subprocess import Popen import click import numpy as np from PIL import ImageEnhance from pyvirtualdisplay.smartdisplay import SmartDisplay import adafruit_blinka_raspberry_pi5_piomatter as piomatter import adafruit_blinka_raspberry_pi5_piomatter.click as piomatter_click from adafruit_blinka_raspberry_pi5_piomatter.pixelmappers import simple_multilane_mapper @click.command @click.option("--scale", type=float, help="The scale factor, larger numbers mean more virtual pixels", default=1) @click.option("--backend", help="The pyvirtualdisplay backend to use", default="xvfb") @click.option("--extra-args", help="Extra arguments to pass to the backend server", default="") @click.option("--rfbport", help="The port number for the --backend xvnc", default=None, type=int) @click.option("--brightness", help="The brightness factor of the image output to the matrix", default=1.0, type=click.FloatRange(min=0.1, max=1.0)) @click.option("--use-xauth/--no-use-xauth", help="If a Xauthority file should be created", default=False) @piomatter_click.standard_options @click.argument("command", nargs=-1) def main(scale, backend, use_xauth, extra_args, rfbport, brightness, width, height, serpentine, rotation, pinout, n_planes, n_temporal_planes, n_addr_lines, n_lanes, command): kwargs = {} if backend == "xvnc": kwargs['rfbport'] = rfbport if extra_args: kwargs['extra_args'] = shlex.split(extra_args) print("xauth", use_xauth) if n_lanes != 2: pixelmap = simple_multilane_mapper(width, height, n_addr_lines, n_lanes) geometry = piomatter.Geometry(width=width, height=height, n_planes=n_planes, n_addr_lines=n_addr_lines, n_temporal_planes=n_temporal_planes, n_lanes=n_lanes, map=pixelmap) else: geometry = piomatter.Geometry(width=width, height=height, n_planes=n_planes, n_addr_lines=n_addr_lines, n_temporal_planes=n_temporal_planes, rotation=rotation, serpentine=serpentine) framebuffer = np.zeros(shape=(geometry.height, geometry.width, 3), dtype=np.uint8) matrix = piomatter.PioMatter(colorspace=piomatter.Colorspace.RGB888Packed, pinout=pinout, framebuffer=framebuffer, geometry=geometry) with SmartDisplay(backend=backend, use_xauth=use_xauth, size=(round(width*scale),round(height*scale)), manage_global_env=False, **kwargs) as disp, Popen(command, env=disp.env()) as proc: while proc.poll() is None: img = disp.grab(autocrop=False) if img is None: continue if brightness != 1.0: darkener = ImageEnhance.Brightness(img) img = darkener.enhance(brightness) img = img.resize((width, height)) framebuffer[:, :] = np.array(img) matrix.show() if __name__ == '__main__': main()
Page last edited March 27, 2025
Text editor powered by tinymce.