diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index ce0100a..88f4902 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -19,7 +19,7 @@ jobs: - name: Set up repository uses: actions/checkout@v4 with: - submodules: false + submodules: true show-progress: false fetch-depth: 1 persist-credentials: false diff --git a/.gitignore b/.gitignore index ec677e5..c25b97b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /*.pio.h /build +/dist +/docs/api *.egg-info diff --git a/examples/adafruit_blinka_raspberry_pi5_piomatter b/examples/adafruit_blinka_raspberry_pi5_piomatter new file mode 120000 index 0000000..cd49910 --- /dev/null +++ b/examples/adafruit_blinka_raspberry_pi5_piomatter @@ -0,0 +1 @@ +../build/lib.linux-x86_64-cpython-311/adafruit_blinka_raspberry_pi5_piomatter/ \ No newline at end of file diff --git a/examples/fbmirror.py b/examples/fbmirror.py index ce287c3..e3d4535 100644 --- a/examples/fbmirror.py +++ b/examples/fbmirror.py @@ -17,7 +17,6 @@ import numpy as np 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 with open("/sys/class/graphics/fb0/virtual_size") as f: screenx, screeny = [int(word) for word in f.read().split(",")] @@ -39,15 +38,10 @@ linux_framebuffer = np.memmap('/dev/fb0',mode='r', shape=(screeny, stride // byt @click.option("--x-offset", "xoffset", type=int, help="The x offset of top left corner of the region to mirror", default=0) @click.option("--y-offset", "yoffset", type=int, help="The y offset of top left corner of the region to mirror", default=0) @piomatter_click.standard_options -def main(xoffset, yoffset, width, height, serpentine, rotation, pinout, n_planes, n_temporal_planes, n_addr_lines, n_lanes): - 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) - framebuffer = np.zeros(shape=(geometry.height, geometry.width), dtype=dtype) - matrix = piomatter.PioMatter(colorspace=piomatter.Colorspace.RGB565, pinout=pinout, framebuffer=framebuffer, geometry=geometry) - +@piomatter_click.make_matrix(colorspace=piomatter.Colorspace.RGB565) +def main(xoffset, yoffset, matrix, framebuffer): + width = matrix.width + height = matrix.height while True: framebuffer[:,:] = linux_framebuffer[yoffset:yoffset+height, xoffset:xoffset+width] matrix.show() diff --git a/examples/virtualdisplay.py b/examples/virtualdisplay.py index be3aed8..bdd45bd 100644 --- a/examples/virtualdisplay.py +++ b/examples/virtualdisplay.py @@ -30,7 +30,6 @@ 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 @@ -42,24 +41,18 @@ from adafruit_blinka_raspberry_pi5_piomatter.pixelmappers import simple_multilan 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 +@piomatter_click.make_matrix(colorspace=piomatter.Colorspace.RGB888Packed) @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): +def main(matrix, framebuffer, scale, backend, use_xauth, extra_args, rfbport, brightness, 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) - 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) + + width = matrix.width + height = matrix.height 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: diff --git a/setup.py b/setup.py index c7c9e4b..f83aea5 100644 --- a/setup.py +++ b/setup.py @@ -1,8 +1,9 @@ # Available at setup time due to pyproject.toml -from pybind11.setup_helpers import Pybind11Extension, build_ext from setuptools import setup from setuptools_scm import get_version +from pybind11.setup_helpers import Pybind11Extension, build_ext + __version__ = get_version() # The main interface is through Pybind11Extension. diff --git a/src/adafruit_blinka_raspberry_pi5_piomatter/click.py b/src/adafruit_blinka_raspberry_pi5_piomatter/click.py index 53482e5..4c3740c 100644 --- a/src/adafruit_blinka_raspberry_pi5_piomatter/click.py +++ b/src/adafruit_blinka_raspberry_pi5_piomatter/click.py @@ -6,9 +6,39 @@ from collections.abc import Callable from typing import Any import click +import numpy as np import adafruit_blinka_raspberry_pi5_piomatter as piomatter +from .pixelmappers import simple_multilane_mapper + + +def make_matrix(*, colorspace): + print("make_matrix", colorspace) + def do_make_matrix(f): + print("do_make_matrix", f) + def wrapper(width, height, serpentine, rotation, pinout, n_planes, n_temporal_planes, n_addr_lines, n_lanes, **click_args): + if colorspace == piomatter.Colorspace.RGB565: + framebuffer = np.zeros((height, width), dtype=np.uint16) + elif colorspace == piomatter.Colorspace.RGB888: + framebuffer = np.zeros((height, width), dtype=np.uint32) + elif colorspace == piomatter.Colorspace.RGB888Packed: + framebuffer = np.zeros((height, width, 3), dtype=np.uint8) + else: + raise ValueError(f"Unsupported colorspace {colorspace!r}") + + if n_lanes != 2: + if serpentine: + raise ValueError("Serpentine is only avaialble with 2 lanes") + 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) + matrix = piomatter.PioMatter(colorspace=colorspace, pinout=pinout, framebuffer=framebuffer, geometry=geometry) + + return f(matrix=matrix, framebuffer=framebuffer, **click_args) + return wrapper + return do_make_matrix class _PybindEnumChoice(click.Choice): def __init__(self, enum, case_sensitive=False): diff --git a/src/include/piomatter/piomatter.h b/src/include/piomatter/piomatter.h index 4ead468..a6f350a 100644 --- a/src/include/piomatter/piomatter.h +++ b/src/include/piomatter/piomatter.h @@ -57,7 +57,7 @@ static uint64_t monotonicns64() { constexpr size_t MAX_XFER = 65532; struct piomatter_base { - piomatter_base() {} + piomatter_base(const matrix_geometry &geometry) : geometry{geometry} {} piomatter_base(const piomatter_base &) = delete; piomatter_base &operator=(const piomatter_base &) = delete; @@ -65,17 +65,18 @@ struct piomatter_base { virtual int show() = 0; double fps; + matrix_geometry geometry; }; template -struct piomatter : piomatter_base { +struct piomatter : public piomatter_base { using buffer_type = std::vector; using bufseq_type = std::vector; piomatter(std::span framebuffer, const matrix_geometry &geometry) - : framebuffer(framebuffer), geometry{geometry}, converter{}, - blitter_thread{} { + : piomatter_base{geometry}, + framebuffer(framebuffer), converter{}, blitter_thread{} { if (geometry.n_addr_lines > std::size(pinout::PIN_ADDR)) { throw std::runtime_error("too many address lines requested"); } @@ -240,7 +241,6 @@ struct piomatter : piomatter_base { std::span framebuffer; bufseq_type buffers[3]; buffer_manager manager{}; - matrix_geometry geometry; colorspace converter; std::thread blitter_thread; std::atomic pending_error_errno; diff --git a/src/pymain.cpp b/src/pymain.cpp index 053aded..7f0e967 100644 --- a/src/pymain.cpp +++ b/src/pymain.cpp @@ -27,6 +27,8 @@ struct PyPiomatter { } } double fps() const { return matter->fps; } + int width() const { return matter->geometry.width; } + int height() const { return matter->geometry.height; } }; template @@ -288,5 +290,7 @@ data is triple-buffered to prevent tearing. )pbdoc") .def_property_readonly("fps", &PyPiomatter::fps, R"pbdoc( The approximate number of matrix refreshes per second. -)pbdoc"); +)pbdoc") + .def_property_readonly("width", &PyPiomatter::width) + .def_property_readonly("height", &PyPiomatter::height); }