This commit is contained in:
7
depends/rpi-rgb-led-matrix/examples-api-use/.gitignore
vendored
Normal file
7
depends/rpi-rgb-led-matrix/examples-api-use/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
demo
|
||||
minimal-example
|
||||
text-example
|
||||
c-example
|
||||
clock
|
||||
scrolling-text-example
|
||||
ledcat
|
||||
60
depends/rpi-rgb-led-matrix/examples-api-use/Makefile
Normal file
60
depends/rpi-rgb-led-matrix/examples-api-use/Makefile
Normal file
@@ -0,0 +1,60 @@
|
||||
CFLAGS=-Wall -O3 -g -Wextra -Wno-unused-parameter
|
||||
CXXFLAGS=$(CFLAGS)
|
||||
OBJECTS=demo-main.o minimal-example.o c-example.o text-example.o scrolling-text-example.o clock.o ledcat.o input-example.o pixel-mover.o
|
||||
BINARIES=demo minimal-example c-example text-example scrolling-text-example clock ledcat input-example pixel-mover
|
||||
|
||||
# Where our library resides. You mostly only need to change the
|
||||
# RGB_LIB_DISTRIBUTION, this is where the library is checked out.
|
||||
RGB_LIB_DISTRIBUTION=..
|
||||
RGB_INCDIR=$(RGB_LIB_DISTRIBUTION)/include
|
||||
RGB_LIBDIR=$(RGB_LIB_DISTRIBUTION)/lib
|
||||
RGB_LIBRARY_NAME=rgbmatrix
|
||||
RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a
|
||||
LDFLAGS+=-L$(RGB_LIBDIR) -l$(RGB_LIBRARY_NAME) -lrt -lm -lpthread
|
||||
|
||||
# To compile image-example
|
||||
MAGICK_CXXFLAGS?=$(shell GraphicsMagick++-config --cppflags --cxxflags)
|
||||
MAGICK_LDFLAGS?=$(shell GraphicsMagick++-config --ldflags --libs)
|
||||
|
||||
all : $(BINARIES)
|
||||
|
||||
$(RGB_LIBRARY): FORCE
|
||||
$(MAKE) -C $(RGB_LIBDIR)
|
||||
|
||||
demo : demo-main.o $(RGB_LIBRARY)
|
||||
$(CXX) $< -o $@ $(LDFLAGS)
|
||||
|
||||
minimal-example : minimal-example.o
|
||||
input-example : input-example.o
|
||||
text-example: text-example.o
|
||||
scrolling-text-example : scrolling-text-example.o
|
||||
clock : clock.o
|
||||
ledcat : ledcat.o
|
||||
pixel-mover : pixel-mover.o
|
||||
|
||||
# All the binaries that have the same name as the object file.q
|
||||
% : %.o $(RGB_LIBRARY)
|
||||
$(CXX) $< -o $@ $(LDFLAGS)
|
||||
|
||||
image-example.o : image-example.cc
|
||||
$(CXX) -I$(RGB_INCDIR) $(CXXFLAGS) $(MAGICK_CXXFLAGS) -c -o $@ $<
|
||||
|
||||
image-example: image-example.o $(RGB_LIBRARY)
|
||||
$(CXX) $< -o $@ $(LDFLAGS) $(MAGICK_LDFLAGS)
|
||||
|
||||
# Since the C example uses the C++ library underneath, which depends on C++
|
||||
# runtime stuff, you still have to also link -lstdc++
|
||||
c-example : c-example.o $(RGB_LIBRARY)
|
||||
$(CC) $< -o $@ $(LDFLAGS) -lstdc++
|
||||
|
||||
%.o : %.cc
|
||||
$(CXX) -I$(RGB_INCDIR) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
%.o : %.c
|
||||
$(CC) -I$(RGB_INCDIR) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f $(OBJECTS) $(BINARIES)
|
||||
|
||||
FORCE:
|
||||
.PHONY: FORCE
|
||||
461
depends/rpi-rgb-led-matrix/examples-api-use/README.md
Normal file
461
depends/rpi-rgb-led-matrix/examples-api-use/README.md
Normal file
@@ -0,0 +1,461 @@
|
||||
Running some demos
|
||||
------------------
|
||||
Let's start by running some demos, then we can dive into code. The
|
||||
[demo-main.cc](demo-main.cc) has some testing demos. Via command line flags,
|
||||
you can choose the display type you have (16x32 or 32x32), and how many you
|
||||
have chained and paralleled. For detailed description of these flags see the
|
||||
[main README section](../README.md#changing-parameters-via-command-line-flags)
|
||||
about it.
|
||||
|
||||
```
|
||||
$ make
|
||||
$ sudo ./demo
|
||||
usage: ./demo <options> -D <demo-nr> [optional parameter]
|
||||
Options:
|
||||
-D <demo-nr> : Always needs to be set
|
||||
--led-gpio-mapping=<name> : Name of GPIO mapping used. Default "regular"
|
||||
--led-rows=<rows> : Panel rows. Typically 8, 16, 32 or 64. (Default: 32).
|
||||
--led-cols=<cols> : Panel columns. Typically 32 or 64. (Default: 32).
|
||||
--led-chain=<chained> : Number of daisy-chained panels. (Default: 1).
|
||||
--led-parallel=<parallel> : Parallel chains. range=1..3 (Default: 1).
|
||||
--led-multiplexing=<0..17> : Mux type: 0=direct; 1=Stripe; 2=Checkered; 3=Spiral; 4=ZStripe; 5=ZnMirrorZStripe; 6=coreman; 7=Kaler2Scan; 8=ZStripeUneven; 9=P10-128x4-Z; 10=QiangLiQ8; 11=InversedZStripe; 12=P10Outdoor1R1G1-1; 13=P10Outdoor1R1G1-2; 14=P10Outdoor1R1G1-3; 15=P10CoremanMapper; 16=P8Outdoor1R1G1; 17=FlippedStripe (Default: 0)
|
||||
--led-pixel-mapper : Semicolon-separated list of pixel-mappers to arrange pixels.
|
||||
Optional params after a colon e.g. "U-mapper;Rotate:90"
|
||||
Available: "Mirror", "Rotate", "U-mapper", "V-mapper". Default: ""
|
||||
--led-pwm-bits=<1..11> : PWM bits (Default: 11).
|
||||
--led-brightness=<percent>: Brightness in percent (Default: 100).
|
||||
--led-scan-mode=<0..1> : 0 = progressive; 1 = interlaced (Default: 0).
|
||||
--led-row-addr-type=<0..4>: 0 = default; 1 = AB-addressed panels; 2 = direct row select; 3 = ABC-addressed panels; 4 = ABC Shift + DE direct (Default: 0).
|
||||
--led-show-refresh : Show refresh rate.
|
||||
--led-limit-refresh=<Hz> : Limit refresh rate to this frequency in Hz. Useful to keep a
|
||||
constant refresh rate on loaded system. 0=no limit. Default: 0
|
||||
--led-inverse : Switch if your matrix has inverse colors on.
|
||||
--led-rgb-sequence : Switch if your matrix has led colors swapped (Default: "RGB")
|
||||
--led-pwm-lsb-nanoseconds : PWM Nanoseconds for LSB (Default: 130)
|
||||
--led-pwm-dither-bits=<0..2> : Time dithering of lower bits (Default: 0)
|
||||
--led-no-hardware-pulse : Don't use hardware pin-pulse generation.
|
||||
--led-panel-type=<name> : Needed to initialize special panels. Supported: 'FM6126A', 'FM6127'
|
||||
--led-slowdown-gpio=<0..4>: Slowdown GPIO. Needed for faster Pis/slower panels (Default: 1).
|
||||
--led-daemon : Make the process run in the background as daemon.
|
||||
--led-no-drop-privs : Don't drop privileges from 'root' after initializing the hardware.
|
||||
Demos, choosen with -D
|
||||
0 - some rotating square
|
||||
1 - forward scrolling an image (-m <scroll-ms>)
|
||||
2 - backward scrolling an image (-m <scroll-ms>)
|
||||
3 - test image: a square
|
||||
4 - Pulsing color
|
||||
5 - Grayscale Block
|
||||
6 - Abelian sandpile model (-m <time-step-ms>)
|
||||
7 - Conway's game of life (-m <time-step-ms>)
|
||||
8 - Langton's ant (-m <time-step-ms>)
|
||||
9 - Volume bars (-m <time-step-ms>)
|
||||
10 - Evolution of color (-m <time-step-ms>)
|
||||
11 - Brightness pulse generator
|
||||
Example:
|
||||
./demo -D 1 runtext.ppm
|
||||
Scrolls the runtext until Ctrl-C is pressed
|
||||
```
|
||||
|
||||
To run the actual demos, you need to run this as root so that the
|
||||
GPIO pins can be accessed; as soon as that established, the program will drop
|
||||
the privileges.
|
||||
|
||||
Here is how demo '1' looks. It requires a ppm (type raw) with a height of
|
||||
32 pixel - it is infinitely scrolled over the screen; for
|
||||
convenience, there is a little runtext.ppm example included:
|
||||
|
||||
$ sudo ./demo -D 1 runtext.ppm
|
||||
|
||||
Here is a video of how it looks
|
||||
[![Runtext][run-vid]](http://youtu.be/OJvEWyvO4ro)
|
||||
|
||||
Other Examples
|
||||
--------------
|
||||
|
||||
There are a few other examples that you can use as starting point for your
|
||||
own exploration of the API. If you just type `make` in this directory, the
|
||||
Makefile will build all of these, so they are ready to use. Some examples
|
||||
need BDF fonts, of which there are a few provided in [../fonts](../fonts).
|
||||
|
||||
Some of these example programs are described in more detail further down this
|
||||
page.
|
||||
|
||||
* [minimal-example](./minimal-example.cc) Good to get started with the API
|
||||
* [image-example](./image-example.cc) How to show an image (requires to install the graphics magic library, see in the header of that demo)
|
||||
* [text-example](./text-example.cc) Reads text from stdin and displays it.
|
||||
* [scrolling-text-example](./scrolling-text-example.cc) Scrolls a text
|
||||
given on the command-line.
|
||||
* [clock](./clock.cc) Shows a clock with choosable date format string in
|
||||
one or multiple lines.
|
||||
* [input-example](./input-example.cc) Example how to use the LED-Matrix but
|
||||
also read inputs from free GPIO-pins. Needed if you build some interactive
|
||||
piece.
|
||||
* [ledcat](./ledcat.cc) LED-cat compatible reading of pixels from stdin.
|
||||
* [pixel-mover](./pixel-mover.cc) Displays pixel on the display
|
||||
and it's expected position on the terminal. Helpful for testing panels and
|
||||
figuring out new multiplexing mappings.
|
||||
Shows single dot or leaves a trail with length passed with `-t` option
|
||||
(think of 'snake').
|
||||
Can move around the pixel with W=Up, S=Down, A=Left, D=Right keys.
|
||||
|
||||
Using the API
|
||||
-------------
|
||||
While there is the demo program and the [utilities](../utils), this code can
|
||||
be used independently as a library to be used in your own programs.
|
||||
The includes are in `include/`, the library to link is built
|
||||
in `lib/`. This is a C++ also with C bindings. There is also a
|
||||
[Python](../bindings/python) and [c#](../bindings/c%23) bindings.
|
||||
|
||||
Getting started
|
||||
---------------
|
||||
The relevant part to start with is to look at
|
||||
[led-matrix.h](../include/led-matrix.h).
|
||||
|
||||
You can would typically use the `RGBMatrix::CreateFromFlags()` factory to
|
||||
create an RGBMatrix and then go from there.
|
||||
|
||||
```C++
|
||||
#include "led-matrix.h"
|
||||
|
||||
using rgb_matrix::RGBMatrix;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
// Set some defaults
|
||||
RGBMatrix::Options my_defaults;
|
||||
my_defaults.hardware_mapping = "regular"; // or e.g. "adafruit-hat" or "adafruit-hat-pwm"
|
||||
my_defaults.chain_length = 3;
|
||||
my_defaults.show_refresh_rate = true;
|
||||
rgb_matrix::RuntimeOptions runtime_defaults;
|
||||
// If you drop privileges, the root user you start the program with
|
||||
// to be able to initialize the hardware will be switched to an unprivileged
|
||||
// user to minimize a potential security attack surface.
|
||||
runtime_defaults.drop_privileges = 1;
|
||||
RGBMatrix *matrix = RGBMatrix::CreateFromFlags(&argc, &argv,
|
||||
&my_defaults,
|
||||
&runtime_defaults);
|
||||
if (matrix == NULL) {
|
||||
PrintMatrixFlags(stderr, my_defaults, runtime_defaults);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// matrix->ApplyPixelMapper(...); // Optional
|
||||
|
||||
// Do your own command line handling with the remaining options.
|
||||
|
||||
// .. now use matrix
|
||||
|
||||
delete matrix; // Make sure to delete it in the end.
|
||||
}
|
||||
```
|
||||
|
||||
The `RGBMatrix` is essentially a canvas, it provides some basic functionality
|
||||
such as `SetPixel()`, `Fill()` or `Clear()`. If you want to do more, you
|
||||
might be interested in functions provided in the
|
||||
[graphics.h](../include/graphics.h) header.
|
||||
|
||||
If you have animations, you might be interested in double-buffering. There is
|
||||
a way to create new canvases with `CreateFrameCanvas()`, and then use
|
||||
`SwapOnVSync()` to change the content atomically. See API documentation for
|
||||
details.
|
||||
|
||||
Start with the [minimal-example.cc](./minimal-example.cc) to start.
|
||||
|
||||
If you are interested in drawing text and the font drawing functions in
|
||||
graphics.h, have a look at the [text example](./text-example.cc):
|
||||
|
||||
```
|
||||
sudo ./text-example -f ../fonts/8x13.bdf
|
||||
hello
|
||||
```
|
||||
|
||||
<img src="../img/text-no-ghosting.jpg" height="100px">
|
||||
|
||||
How about a clock ?
|
||||
|
||||
```
|
||||
sudo ./clock -f ../fonts/7x13.bdf --led-chain=2 -d "%A" -d "%H:%M:%S"
|
||||
```
|
||||
<img src="../img/time-display.jpg" height="100px">
|
||||
|
||||
Fonts are in a human readable and editbable `*.bdf` format. There are some
|
||||
public domain fonts available in the [`../fonts/`](../fonts) directory. Any
|
||||
other fonts you might want to use or scale to the size you need can be
|
||||
converted to a BDF format (either with a font editor or the [otf2bdf] tool).
|
||||
|
||||
Here is an example how you could create a 30pixel high BDF font from some
|
||||
TrueType font:
|
||||
|
||||
```bash
|
||||
otf2bdf -v -o myfont.bdf -r 72 -p 30 /path/to/font-Bold.ttf
|
||||
```
|
||||
|
||||
Integrating in your own application
|
||||
-----------------------------------
|
||||
Until this library shows up in your favorite Linux distribution, you can just
|
||||
include the library via github; it is pretty easy to be up-to-date.
|
||||
|
||||
I suggest to add this code as a sub-module in your git repository. That way
|
||||
you can use that particular version and easily update it if there are changes:
|
||||
|
||||
git submodule add https://github.com/hzeller/rpi-rgb-led-matrix.git matrix
|
||||
|
||||
(Read more about how to use [submodules in git][git-submodules])
|
||||
|
||||
This will check out the repository in a subdirectory `matrix/`.
|
||||
The library to build would be in directory `matrix/lib`, so let's hook that
|
||||
into your toplevel Makefile.
|
||||
I suggest to set up some variables like this; you only need to change the
|
||||
location `RGB_LIB_DISTRIBUTION` is pointing to; in the sub-module example, this
|
||||
was the `matrix` directory:
|
||||
|
||||
RGB_LIB_DISTRIBUTION=matrix
|
||||
RGB_INCDIR=$(RGB_LIB_DISTRIBUTION)/include
|
||||
RGB_LIBDIR=$(RGB_LIB_DISTRIBUTION)/lib
|
||||
RGB_LIBRARY_NAME=rgbmatrix
|
||||
RGB_LIBRARY=$(RGB_LIBDIR)/lib$(RGB_LIBRARY_NAME).a
|
||||
LDFLAGS+=-L$(RGB_LIBDIR) -l$(RGB_LIBRARY_NAME) -lrt -lm -lpthread
|
||||
|
||||
Also, you want to add a target to build the libary in your sub-module
|
||||
|
||||
# (FYI: Make sure, there is a TAB-character in front of the $(MAKE))
|
||||
$(RGB_LIBRARY):
|
||||
$(MAKE) -C $(RGB_LIBDIR)
|
||||
|
||||
Now, your final binary needs to depend on your objects and also the
|
||||
`$(RGB_LIBRARY)`
|
||||
|
||||
my-binary : $(OBJECTS) $(RGB_LIBRARY)
|
||||
$(CXX) $(CXXFLAGS) $(OBJECTS) -o $@ $(LDFLAGS)
|
||||
|
||||
As an example, see the [PixelPusher implementation][pixelpush] which is using
|
||||
this library in a git sub-module.
|
||||
|
||||
If you are writing your own Makefile, make sure to pass the `-O3` option to
|
||||
the compiler to make sure to generate fast code.
|
||||
|
||||
Note, all the types provided are in the `rgb_matrix` namespace. That way, they
|
||||
won't clash with other types you might use in your code; in particular pretty
|
||||
common names such as `GPIO` or `Canvas` might run into clashing trouble.
|
||||
|
||||
Anyway, for convenience you just might add using-declarations in your
|
||||
code:
|
||||
|
||||
// Types exported by the RGB-Matrix library.
|
||||
using rgb_matrix::Canvas;
|
||||
using rgb_matrix::GPIO;
|
||||
using rgb_matrix::RGBMatrix;
|
||||
|
||||
Or, if you are lazy, just import the whole namespace:
|
||||
|
||||
using namespace rgb_matrix;
|
||||
|
||||
Read the [`minimal-example.cc`](./minimal-example.cc) to get started, then
|
||||
have a look into [`demo-main.cc`](./demo-main.cc).
|
||||
|
||||
## Remapping coordinates ##
|
||||
|
||||
You might choose a different physical layout than the wiring provides.
|
||||
|
||||
There is an option `--led-pixel-mapper` that allows you to choose between
|
||||
some re-mapping options, and also programmatic ways to do so.
|
||||
|
||||
### Standard mappers
|
||||
|
||||
#### U-mapper (U-shape connection)
|
||||
Say you have 4 displays with 32x32 and only a single output
|
||||
like with a Raspberry Pi 1 or the Adafruit HAT -- if we chain
|
||||
them, we get a display 32 pixel high, (4*32)=128 pixel long. If we arrange
|
||||
the boards in a U-shape so that they form a square, we get a logical display
|
||||
of 64x64 pixels:
|
||||
|
||||
<img src="../img/chained-64x64.jpg" width="400px"> In action:
|
||||
[![PixelPusher video][pp-vid]](http://youtu.be/ZglGuMaKvpY)
|
||||
|
||||
```
|
||||
So the following chain (Viewed looking at the LED-side of the panels)
|
||||
[<][<][<][<] }- Raspbery Pi connector
|
||||
|
||||
is arranged in this U-shape (on its side)
|
||||
[<][<] }----- Raspberry Pi connector
|
||||
[>][>]
|
||||
```
|
||||
|
||||
Now we need to internally map pixels the pixels so that the 'folded' 128x32
|
||||
screen behaves like a 64x64 screen.
|
||||
|
||||
There is a pixel-mapper that can help with this "U-Arrangement", you choose
|
||||
it with `--led-pixel-mapper=U-mapper`. So in this particular case,
|
||||
|
||||
```
|
||||
./demo --led-chain=4 --led-pixel-mapper="U-mapper"
|
||||
```
|
||||
|
||||
This works for longer and more than one chain as well. Here an arrangement with
|
||||
two chains with 8 panels each
|
||||
|
||||
```
|
||||
[<][<][<][<] }--- Pi connector #1
|
||||
[>][>][>][>]
|
||||
[<][<][<][<] }--- Pi connector #2
|
||||
[>][>][>][>]
|
||||
```
|
||||
|
||||
(`--led-chain=8 --led-parallel=2 --led-pixel-mapper="U-mapper"`).
|
||||
|
||||
#### V-mapper and Vmapper:Z (Vertical arrangement)
|
||||
|
||||
By default, when you add panels on a chain, they are added horizontally.
|
||||
If you have 2 panels of 64x32, you get 128x32.
|
||||
The V-mapper allows the stacking to be vertical and not horizontal and
|
||||
get the 64x64 you might want.
|
||||
|
||||
By default, all the panels are correct side up, and you need more cable length
|
||||
as you need to cross back to the start of the next panel.
|
||||
If you wish to use shorter cables, you can add use Vmapper:Z which will give
|
||||
you serpentine cabling and every other panel will be upside down (see below
|
||||
for an example).
|
||||
|
||||
It is compatible with parallel chains, so you can have multiple stacks
|
||||
of panels all building a coherent overall display.
|
||||
|
||||
Here an example with 3 chains of 4 panels (128x64) for a total of about
|
||||
98k display pixels.
|
||||
|
||||
```
|
||||
./demo --led-rows=64 --led-cols=128 --led-chain=4 -led-parallel=3 --led-pixel-mapper=V-mapper -D0
|
||||
```
|
||||
|
||||
Viewed looking the LED-side of the panels:
|
||||
|
||||
```
|
||||
Vmapper Vmapper:Z
|
||||
|
||||
[O < I] [O < I] [O < I] [I > O] [I > O] [I > O]
|
||||
,---^ ,---^ ,---^ ^ ^ ^
|
||||
[O < I] [O < I] [O < I] [O < I] [O < I] [O < I]
|
||||
,---^ ,---^ ,---^ ^ ^ ^
|
||||
[O < I] [O < I] [O < I] [I > O] [I > O] [I > O]
|
||||
,---^ ,---^ ,---^ ^ ^ ^
|
||||
[O < I] [O < I] [O < I] [O < I] [O < I] [O < I]
|
||||
^ ^ ^ ^ ^ ^
|
||||
#1 #2 #3 #1 #2 #3
|
||||
Pi connector (three parallel chains of len 4)
|
||||
```
|
||||
|
||||
(This is also a good time to notice that 384x256 with 12 128x64 panels, is probably an
|
||||
upper limit of what you can reasonably output without having an unusable fresh
|
||||
rate (Try these options to help: --led-pwm-bits=7 --led-pwm-dither-bits=1 and get about 100Hz)).
|
||||
|
||||
This shows the wiring of a 3x5 Vmapper:Z array built by Marc MERLIN, using 15x 64x32 panels:
|
||||

|
||||
With --led-pwm-bits=7 --led-pwm-dither-bits=1, it gets a better 300Hz refresh
|
||||
but only offers around 31K pixels instead of 98K pixels in the previous example.
|
||||
|
||||
Please note that Vmapper can also be used to improve the refresh rate of a long
|
||||
display even if it is only one panel high (e.g. for a text running output) by
|
||||
splitting the load into multiple parallel chains.
|
||||
|
||||
```
|
||||
|
||||
[O < I] [O < I] [O < I]
|
||||
^ ^ ^
|
||||
#1 #2 #3 Pi connector (three parallel chains of len 1)
|
||||
```
|
||||
|
||||
#### Rotate
|
||||
|
||||
The "Rotate" mapper allows you to rotate your screen. It takes an angle
|
||||
as parameter after a colon:
|
||||
|
||||
```
|
||||
./demo --led-pixel-mapper="Rotate:90"
|
||||
```
|
||||
|
||||
#### Mirror
|
||||
|
||||
The 'Mirror' mapper allows to mirror the output horizontally or vertically.
|
||||
Without parameter, it mirrors horizontally. The parameter is a single character
|
||||
'H' or 'V' for horizontal or vertical mirroring.
|
||||
|
||||
```
|
||||
./demo --led-pixel-mapper="Mirror:H"
|
||||
```
|
||||
|
||||
#### Combining Mappers
|
||||
|
||||
You can chain multiple mappers in the configuration, by separating them
|
||||
with a semicolon. The mappers are applied in the sequence you give them, so
|
||||
if you want to arrange a couple of panels with the U-arrangement, and then
|
||||
rotate the resulting screen, use
|
||||
|
||||
```
|
||||
./demo --led-chain=8 --led-parallel=3 --led-pixel-mapper="U-mapper;Rotate:90"
|
||||
```
|
||||
|
||||
Here, we first create a 128x192 screen (4 panels wide (`4*32=128`),
|
||||
with three folded chains (`6*32=192`)) and then rotate it by 90 degrees to
|
||||
get a 192x128 screen.
|
||||
|
||||
#### Programmatic access
|
||||
|
||||
If you want to choose these mappers programmatically from your program and
|
||||
not via the flags, you can do this by setting the `pixel_mapper_config` option
|
||||
in the options struct in C++ or Python.
|
||||
|
||||
```
|
||||
options.pixel_mapper_config = "Rotate:90";
|
||||
```
|
||||
|
||||
### Writing your own mappers
|
||||
|
||||
If you want to write your own mappers, e.g. if you have a fancy panel
|
||||
arrangement, you can do so using the API provided.
|
||||
|
||||
In the API, there is an interface to implement,
|
||||
a [`PixelMapper`](../include/pixel-mapper.h) that allows to program
|
||||
re-arrangements of pixels in any way. You can plug such an implementation of
|
||||
a `PixelMapper` into the RGBMatrix to use it:
|
||||
|
||||
```
|
||||
bool RGBMatrix::ApplyPixelMapper(const PixelMapper *mapper);
|
||||
```
|
||||
|
||||
If you want, you can also register your PixelMapper globally before you
|
||||
parse the command line options; then this pixel-mapper is automatically
|
||||
provided in the `--led-pixel-mapper` command line option:
|
||||
|
||||
```
|
||||
RegisterPixelMapper(new MyOwnPixelMapper());
|
||||
RGBMatrix *matrix = RGBMatrix::CreateFromFlags(...);
|
||||
```
|
||||
|
||||
Now your mapper can be used alongside (and combined with) the standard
|
||||
mappers already there (e.g. "U-mapper" or "Rotate"). Your mapper can have
|
||||
parameters: In the command-line flag, parameters provided after `:` are passed
|
||||
as-is to your `SetParameters()` implementation
|
||||
(e.g. using `--led-pixel-mapper="Rotate:90"`, the `Rotate` mapper
|
||||
gets a parameter string `"90"` as parameter).
|
||||
|
||||
#### Multiplex Mappers
|
||||
|
||||
Sometimes you even need this for the panel itself: In some panels
|
||||
(typically the 'outdoor panels', often with 1:4 multiplexing) the pixels
|
||||
are not mapped in a straight-forward way, but in a snake arrangement for
|
||||
instance.
|
||||
|
||||
There are simplified pixel mappers for this purpose, the
|
||||
[multiplex mappers](../lib/multiplex-mappers.cc). These are defined there
|
||||
and then can be accessed via the command line flag `--led-multiplexing=...`.
|
||||
|
||||
If you find that whatever parameter you give to `--led-multiplexing=` doesn't
|
||||
work, you might need to write your own mapper (extend `MultiplexMapperBase`
|
||||
and implement the one method `MapSinglePanel()`). Then register them with
|
||||
the `CreateMultiplexMapperList()` function in that file. When you do this,
|
||||
this will automatically become available in the `--led-multiplexing=` command
|
||||
line option in C++ and Python.
|
||||
|
||||
[run-vid]: ../img/running-vid.jpg
|
||||
[git-submodules]: http://git-scm.com/book/en/Git-Tools-Submodules
|
||||
[pixelpush]: https://github.com/hzeller/rpi-matrix-pixelpusher
|
||||
[pp-vid]: ../img/pixelpusher-vid.jpg
|
||||
[otf2bdf]: https://github.com/jirutka/otf2bdf
|
||||
61
depends/rpi-rgb-led-matrix/examples-api-use/c-example.c
Normal file
61
depends/rpi-rgb-led-matrix/examples-api-use/c-example.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/* -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* Using the C-API of this library.
|
||||
*
|
||||
*/
|
||||
#include "led-matrix-c.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
struct RGBLedMatrixOptions options;
|
||||
struct RGBLedMatrix *matrix;
|
||||
struct LedCanvas *offscreen_canvas;
|
||||
int width, height;
|
||||
int x, y, i;
|
||||
|
||||
memset(&options, 0, sizeof(options));
|
||||
options.rows = 32;
|
||||
options.chain_length = 1;
|
||||
|
||||
/* This supports all the led commandline options. Try --led-help */
|
||||
matrix = led_matrix_create_from_options(&options, &argc, &argv);
|
||||
if (matrix == NULL)
|
||||
return 1;
|
||||
|
||||
/* Let's do an example with double-buffering. We create one extra
|
||||
* buffer onto which we draw, which is then swapped on each refresh.
|
||||
* This is typically a good aproach for animations and such.
|
||||
*/
|
||||
offscreen_canvas = led_matrix_create_offscreen_canvas(matrix);
|
||||
|
||||
led_canvas_get_size(offscreen_canvas, &width, &height);
|
||||
|
||||
fprintf(stderr, "Size: %dx%d. Hardware gpio mapping: %s\n",
|
||||
width, height, options.hardware_mapping);
|
||||
|
||||
for (i = 0; i < 1000; ++i) {
|
||||
for (y = 0; y < height; ++y) {
|
||||
for (x = 0; x < width; ++x) {
|
||||
led_canvas_set_pixel(offscreen_canvas, x, y, i & 0xff, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now, we swap the canvas. We give swap_on_vsync the buffer we
|
||||
* just have drawn into, and wait until the next vsync happens.
|
||||
* we get back the unused buffer to which we'll draw in the next
|
||||
* iteration.
|
||||
*/
|
||||
offscreen_canvas = led_matrix_swap_on_vsync(matrix, offscreen_canvas);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure to always call led_matrix_delete() in the end to reset the
|
||||
* display. Installing signal handlers for defined exit is a good idea.
|
||||
*/
|
||||
led_matrix_delete(matrix);
|
||||
|
||||
return 0;
|
||||
}
|
||||
198
depends/rpi-rgb-led-matrix/examples-api-use/clock.cc
Normal file
198
depends/rpi-rgb-led-matrix/examples-api-use/clock.cc
Normal file
@@ -0,0 +1,198 @@
|
||||
// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
|
||||
// Example of a clock. This is very similar to the text-example,
|
||||
// except that it shows the time :)
|
||||
//
|
||||
// This code is public domain
|
||||
// (but note, that the led-matrix library this depends on is GPL v2)
|
||||
|
||||
#include "led-matrix.h"
|
||||
#include "graphics.h"
|
||||
|
||||
#include <getopt.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
using namespace rgb_matrix;
|
||||
|
||||
volatile bool interrupt_received = false;
|
||||
static void InterruptHandler(int signo) {
|
||||
interrupt_received = true;
|
||||
}
|
||||
|
||||
static int usage(const char *progname) {
|
||||
fprintf(stderr, "usage: %s [options]\n", progname);
|
||||
fprintf(stderr, "Reads text from stdin and displays it. "
|
||||
"Empty string: clear screen\n");
|
||||
fprintf(stderr, "Options:\n");
|
||||
fprintf(stderr,
|
||||
"\t-d <time-format> : Default '%%H:%%M'. See strftime()\n"
|
||||
"\t Can be provided multiple times for multiple "
|
||||
"lines\n"
|
||||
"\t-f <font-file> : Use given font.\n"
|
||||
"\t-x <x-origin> : X-Origin of displaying text (Default: 0)\n"
|
||||
"\t-y <y-origin> : Y-Origin of displaying text (Default: 0)\n"
|
||||
"\t-s <line-spacing> : Extra spacing between lines when multiple -d given\n"
|
||||
"\t-S <spacing> : Extra spacing between letters (Default: 0)\n"
|
||||
"\t-C <r,g,b> : Color. Default 255,255,0\n"
|
||||
"\t-B <r,g,b> : Background-Color. Default 0,0,0\n"
|
||||
"\t-O <r,g,b> : Outline-Color, e.g. to increase contrast.\n"
|
||||
"\n"
|
||||
);
|
||||
rgb_matrix::PrintMatrixFlags(stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool parseColor(Color *c, const char *str) {
|
||||
return sscanf(str, "%hhu,%hhu,%hhu", &c->r, &c->g, &c->b) == 3;
|
||||
}
|
||||
|
||||
static bool FullSaturation(const Color &c) {
|
||||
return (c.r == 0 || c.r == 255)
|
||||
&& (c.g == 0 || c.g == 255)
|
||||
&& (c.b == 0 || c.b == 255);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
RGBMatrix::Options matrix_options;
|
||||
rgb_matrix::RuntimeOptions runtime_opt;
|
||||
if (!rgb_matrix::ParseOptionsFromFlags(&argc, &argv,
|
||||
&matrix_options, &runtime_opt)) {
|
||||
return usage(argv[0]);
|
||||
}
|
||||
|
||||
// We accept multiple format lines
|
||||
|
||||
std::vector<std::string> format_lines;
|
||||
Color color(255, 255, 0);
|
||||
Color bg_color(0, 0, 0);
|
||||
Color outline_color(0,0,0);
|
||||
bool with_outline = false;
|
||||
|
||||
const char *bdf_font_file = NULL;
|
||||
int x_orig = 0;
|
||||
int y_orig = 0;
|
||||
int letter_spacing = 0;
|
||||
int line_spacing = 0;
|
||||
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "x:y:f:C:B:O:s:S:d:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'd': format_lines.push_back(optarg); break;
|
||||
case 'x': x_orig = atoi(optarg); break;
|
||||
case 'y': y_orig = atoi(optarg); break;
|
||||
case 'f': bdf_font_file = strdup(optarg); break;
|
||||
case 's': line_spacing = atoi(optarg); break;
|
||||
case 'S': letter_spacing = atoi(optarg); break;
|
||||
case 'C':
|
||||
if (!parseColor(&color, optarg)) {
|
||||
fprintf(stderr, "Invalid color spec: %s\n", optarg);
|
||||
return usage(argv[0]);
|
||||
}
|
||||
break;
|
||||
case 'B':
|
||||
if (!parseColor(&bg_color, optarg)) {
|
||||
fprintf(stderr, "Invalid background color spec: %s\n", optarg);
|
||||
return usage(argv[0]);
|
||||
}
|
||||
break;
|
||||
case 'O':
|
||||
if (!parseColor(&outline_color, optarg)) {
|
||||
fprintf(stderr, "Invalid outline color spec: %s\n", optarg);
|
||||
return usage(argv[0]);
|
||||
}
|
||||
with_outline = true;
|
||||
break;
|
||||
default:
|
||||
return usage(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (format_lines.empty()) {
|
||||
format_lines.push_back("%H:%M");
|
||||
}
|
||||
|
||||
if (bdf_font_file == NULL) {
|
||||
fprintf(stderr, "Need to specify BDF font-file with -f\n");
|
||||
return usage(argv[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Load font. This needs to be a filename with a bdf bitmap font.
|
||||
*/
|
||||
rgb_matrix::Font font;
|
||||
if (!font.LoadFont(bdf_font_file)) {
|
||||
fprintf(stderr, "Couldn't load font '%s'\n", bdf_font_file);
|
||||
return 1;
|
||||
}
|
||||
rgb_matrix::Font *outline_font = NULL;
|
||||
if (with_outline) {
|
||||
outline_font = font.CreateOutlineFont();
|
||||
}
|
||||
|
||||
RGBMatrix *matrix = RGBMatrix::CreateFromOptions(matrix_options, runtime_opt);
|
||||
if (matrix == NULL)
|
||||
return 1;
|
||||
|
||||
const bool all_extreme_colors = (matrix_options.brightness == 100)
|
||||
&& FullSaturation(color)
|
||||
&& FullSaturation(bg_color)
|
||||
&& FullSaturation(outline_color);
|
||||
if (all_extreme_colors)
|
||||
matrix->SetPWMBits(1);
|
||||
|
||||
const int x = x_orig;
|
||||
int y = y_orig;
|
||||
|
||||
FrameCanvas *offscreen = matrix->CreateFrameCanvas();
|
||||
|
||||
char text_buffer[256];
|
||||
struct timespec next_time;
|
||||
next_time.tv_sec = time(NULL);
|
||||
next_time.tv_nsec = 0;
|
||||
struct tm tm;
|
||||
|
||||
signal(SIGTERM, InterruptHandler);
|
||||
signal(SIGINT, InterruptHandler);
|
||||
|
||||
while (!interrupt_received) {
|
||||
offscreen->Fill(bg_color.r, bg_color.g, bg_color.b);
|
||||
localtime_r(&next_time.tv_sec, &tm);
|
||||
|
||||
int line_offset = 0;
|
||||
for (const std::string &line : format_lines) {
|
||||
strftime(text_buffer, sizeof(text_buffer), line.c_str(), &tm);
|
||||
if (outline_font) {
|
||||
rgb_matrix::DrawText(offscreen, *outline_font,
|
||||
x - 1, y + font.baseline() + line_offset,
|
||||
outline_color, NULL, text_buffer,
|
||||
letter_spacing - 2);
|
||||
}
|
||||
rgb_matrix::DrawText(offscreen, font,
|
||||
x, y + font.baseline() + line_offset,
|
||||
color, NULL, text_buffer,
|
||||
letter_spacing);
|
||||
line_offset += font.height() + line_spacing;
|
||||
}
|
||||
|
||||
// Wait until we're ready to show it.
|
||||
clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &next_time, NULL);
|
||||
|
||||
// Atomic swap with double buffer
|
||||
offscreen = matrix->SwapOnVSync(offscreen);
|
||||
|
||||
next_time.tv_sec += 1;
|
||||
}
|
||||
|
||||
// Finished. Shut down the RGB matrix.
|
||||
delete matrix;
|
||||
|
||||
write(STDOUT_FILENO, "\n", 1); // Create a fresh new line after ^C on screen
|
||||
return 0;
|
||||
}
|
||||
1190
depends/rpi-rgb-led-matrix/examples-api-use/demo-main.cc
Normal file
1190
depends/rpi-rgb-led-matrix/examples-api-use/demo-main.cc
Normal file
File diff suppressed because it is too large
Load Diff
160
depends/rpi-rgb-led-matrix/examples-api-use/image-example.cc
Normal file
160
depends/rpi-rgb-led-matrix/examples-api-use/image-example.cc
Normal file
@@ -0,0 +1,160 @@
|
||||
// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
|
||||
//
|
||||
// Example how to display an image, including animated images using
|
||||
// ImageMagick. For a full utility that does a few more things, have a look
|
||||
// at the led-image-viewer in ../utils
|
||||
//
|
||||
// Showing an image is not so complicated, essentially just copy all the
|
||||
// pixels to the canvas. How to get the pixels ? In this example we're using
|
||||
// the graphicsmagick library as universal image loader library that
|
||||
// can also deal with animated images.
|
||||
// You can of course do your own image loading or use some other library.
|
||||
//
|
||||
// This requires an external dependency, so install these first before you
|
||||
// can call `make image-example`
|
||||
// sudo apt-get update
|
||||
// sudo apt-get install libgraphicsmagick++-dev libwebp-dev -y
|
||||
// make image-example
|
||||
|
||||
#include "led-matrix.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <exception>
|
||||
#include <Magick++.h>
|
||||
#include <magick/image.h>
|
||||
|
||||
using rgb_matrix::Canvas;
|
||||
using rgb_matrix::RGBMatrix;
|
||||
using rgb_matrix::FrameCanvas;
|
||||
|
||||
// Make sure we can exit gracefully when Ctrl-C is pressed.
|
||||
volatile bool interrupt_received = false;
|
||||
static void InterruptHandler(int signo) {
|
||||
interrupt_received = true;
|
||||
}
|
||||
|
||||
using ImageVector = std::vector<Magick::Image>;
|
||||
|
||||
// Given the filename, load the image and scale to the size of the
|
||||
// matrix.
|
||||
// // If this is an animated image, the resutlting vector will contain multiple.
|
||||
static ImageVector LoadImageAndScaleImage(const char *filename,
|
||||
int target_width,
|
||||
int target_height) {
|
||||
ImageVector result;
|
||||
|
||||
ImageVector frames;
|
||||
try {
|
||||
readImages(&frames, filename);
|
||||
} catch (std::exception &e) {
|
||||
if (e.what())
|
||||
fprintf(stderr, "%s\n", e.what());
|
||||
return result;
|
||||
}
|
||||
|
||||
if (frames.empty()) {
|
||||
fprintf(stderr, "No image found.");
|
||||
return result;
|
||||
}
|
||||
|
||||
// Animated images have partial frames that need to be put together
|
||||
if (frames.size() > 1) {
|
||||
Magick::coalesceImages(&result, frames.begin(), frames.end());
|
||||
} else {
|
||||
result.push_back(frames[0]); // just a single still image.
|
||||
}
|
||||
|
||||
for (Magick::Image &image : result) {
|
||||
image.scale(Magick::Geometry(target_width, target_height));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Copy an image to a Canvas. Note, the RGBMatrix is implementing the Canvas
|
||||
// interface as well as the FrameCanvas we use in the double-buffering of the
|
||||
// animted image.
|
||||
void CopyImageToCanvas(const Magick::Image &image, Canvas *canvas) {
|
||||
const int offset_x = 0, offset_y = 0; // If you want to move the image.
|
||||
// Copy all the pixels to the canvas.
|
||||
for (size_t y = 0; y < image.rows(); ++y) {
|
||||
for (size_t x = 0; x < image.columns(); ++x) {
|
||||
const Magick::Color &c = image.pixelColor(x, y);
|
||||
if (c.alphaQuantum() < 256) {
|
||||
canvas->SetPixel(x + offset_x, y + offset_y,
|
||||
ScaleQuantumToChar(c.redQuantum()),
|
||||
ScaleQuantumToChar(c.greenQuantum()),
|
||||
ScaleQuantumToChar(c.blueQuantum()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// An animated image has to constantly swap to the next frame.
|
||||
// We're using double-buffering and fill an offscreen buffer first, then show.
|
||||
void ShowAnimatedImage(const ImageVector &images, RGBMatrix *matrix) {
|
||||
FrameCanvas *offscreen_canvas = matrix->CreateFrameCanvas();
|
||||
while (!interrupt_received) {
|
||||
for (const auto &image : images) {
|
||||
if (interrupt_received) break;
|
||||
CopyImageToCanvas(image, offscreen_canvas);
|
||||
offscreen_canvas = matrix->SwapOnVSync(offscreen_canvas);
|
||||
usleep(image.animationDelay() * 10000); // 1/100s converted to usec
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int usage(const char *progname) {
|
||||
fprintf(stderr, "Usage: %s [led-matrix-options] <image-filename>\n",
|
||||
progname);
|
||||
rgb_matrix::PrintMatrixFlags(stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
Magick::InitializeMagick(*argv);
|
||||
|
||||
// Initialize the RGB matrix with
|
||||
RGBMatrix::Options matrix_options;
|
||||
rgb_matrix::RuntimeOptions runtime_opt;
|
||||
if (!rgb_matrix::ParseOptionsFromFlags(&argc, &argv,
|
||||
&matrix_options, &runtime_opt)) {
|
||||
return usage(argv[0]);
|
||||
}
|
||||
|
||||
if (argc != 2)
|
||||
return usage(argv[0]);
|
||||
const char *filename = argv[1];
|
||||
|
||||
signal(SIGTERM, InterruptHandler);
|
||||
signal(SIGINT, InterruptHandler);
|
||||
|
||||
RGBMatrix *matrix = RGBMatrix::CreateFromOptions(matrix_options, runtime_opt);
|
||||
if (matrix == NULL)
|
||||
return 1;
|
||||
|
||||
ImageVector images = LoadImageAndScaleImage(filename,
|
||||
matrix->width(),
|
||||
matrix->height());
|
||||
switch (images.size()) {
|
||||
case 0: // failed to load image.
|
||||
break;
|
||||
case 1: // Simple example: one image to show
|
||||
CopyImageToCanvas(images[0], matrix);
|
||||
while (!interrupt_received) sleep(1000); // Until Ctrl-C is pressed
|
||||
break;
|
||||
default: // More than one image: this is an animation.
|
||||
ShowAnimatedImage(images, matrix);
|
||||
break;
|
||||
}
|
||||
|
||||
matrix->Clear();
|
||||
delete matrix;
|
||||
|
||||
return 0;
|
||||
}
|
||||
64
depends/rpi-rgb-led-matrix/examples-api-use/input-example.cc
Normal file
64
depends/rpi-rgb-led-matrix/examples-api-use/input-example.cc
Normal file
@@ -0,0 +1,64 @@
|
||||
// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
|
||||
// Small example how to use the input bits
|
||||
//
|
||||
// This code is public domain
|
||||
// (but note, that the led-matrix library this depends on is GPL v2)
|
||||
|
||||
#include "led-matrix.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
|
||||
using rgb_matrix::RGBMatrix;
|
||||
using rgb_matrix::Canvas;
|
||||
|
||||
volatile bool interrupt_received = false;
|
||||
static void InterruptHandler(int signo) {
|
||||
interrupt_received = true;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
RGBMatrix::Options defaults;
|
||||
defaults.hardware_mapping = "regular"; // or e.g. "adafruit-hat"
|
||||
defaults.rows = 32;
|
||||
defaults.chain_length = 1;
|
||||
defaults.parallel = 1;
|
||||
RGBMatrix *matrix = RGBMatrix::CreateFromFlags(&argc, &argv, &defaults);
|
||||
if (matrix == NULL)
|
||||
return 1;
|
||||
|
||||
// It is always good to set up a signal handler to cleanly exit when we
|
||||
// receive a CTRL-C for instance.
|
||||
signal(SIGTERM, InterruptHandler);
|
||||
signal(SIGINT, InterruptHandler);
|
||||
|
||||
// Let's request all input bits and see which are actually available.
|
||||
// This will differ depending on which hardware mapping you use and how
|
||||
// many parallel chains you have.
|
||||
const uint64_t available_inputs = matrix->RequestInputs(0xffffffff);
|
||||
fprintf(stderr, "Available GPIO-bits: ");
|
||||
for (int b = 0; b < 32; ++b) {
|
||||
if (available_inputs & (1<<b))
|
||||
fprintf(stderr, "%d ", b);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
while (!interrupt_received) {
|
||||
// Block and wait until any input bit changed or 100ms passed
|
||||
uint32_t inputs = matrix->AwaitInputChange(100);
|
||||
|
||||
// Minimal output: let's show the bits with LEDs in the first row
|
||||
for (int b = 0; b < 32; ++b) {
|
||||
uint8_t col = (inputs & (1<<b)) ? 255 : 0;
|
||||
matrix->SetPixel(32-b, 0, col, col, col);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "Exiting.\n");
|
||||
matrix->Clear();
|
||||
delete matrix;
|
||||
|
||||
return 0;
|
||||
}
|
||||
82
depends/rpi-rgb-led-matrix/examples-api-use/ledcat.cc
Normal file
82
depends/rpi-rgb-led-matrix/examples-api-use/ledcat.cc
Normal file
@@ -0,0 +1,82 @@
|
||||
// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
|
||||
// A program that reads frames form STDIN as RGB24, much like
|
||||
// https://github.com/polyfloyd/ledcat does.
|
||||
//
|
||||
// This code is public domain
|
||||
// (but note, that the led-matrix library this depends on is GPL v2)
|
||||
|
||||
#include "led-matrix.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define FPS 60
|
||||
|
||||
using rgb_matrix::RGBMatrix;
|
||||
using rgb_matrix::Canvas;
|
||||
|
||||
volatile bool interrupt_received = false;
|
||||
static void InterruptHandler(int signo) {
|
||||
interrupt_received = true;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
RGBMatrix::Options defaults;
|
||||
defaults.hardware_mapping = "regular"; // or e.g. "adafruit-hat"
|
||||
defaults.rows = 32;
|
||||
defaults.chain_length = 1;
|
||||
defaults.parallel = 1;
|
||||
Canvas *canvas = RGBMatrix::CreateFromFlags(&argc, &argv, &defaults);
|
||||
if (canvas == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// It is always good to set up a signal handler to cleanly exit when we
|
||||
// receive a CTRL-C for instance. The DrawOnCanvas() routine is looking
|
||||
// for that.
|
||||
signal(SIGTERM, InterruptHandler);
|
||||
signal(SIGINT, InterruptHandler);
|
||||
|
||||
ssize_t frame_size = canvas->width() * canvas->height() * 3;
|
||||
uint8_t buf[frame_size];
|
||||
|
||||
while (1) {
|
||||
struct timespec start;
|
||||
timespec_get(&start, TIME_UTC);
|
||||
|
||||
ssize_t nread;
|
||||
ssize_t total_nread = 0;
|
||||
while ((nread = read(STDIN_FILENO, &buf[total_nread], frame_size - total_nread)) > 0) {
|
||||
if (interrupt_received) {
|
||||
return 1;
|
||||
}
|
||||
total_nread += nread;
|
||||
}
|
||||
if (total_nread < frame_size){
|
||||
break;
|
||||
}
|
||||
|
||||
for (int y = 0; y < canvas->height(); y++) {
|
||||
for (int x = 0; x < canvas->width(); x++) {
|
||||
uint8_t *p = &buf[(y * canvas->width() + x) * 3];
|
||||
uint8_t r = *(p+0), g = *(p+1), b = *(p+2);
|
||||
canvas->SetPixel(x, y, r, g, b);
|
||||
}
|
||||
}
|
||||
|
||||
struct timespec end;
|
||||
timespec_get(&end, TIME_UTC);
|
||||
long tudiff = (end.tv_nsec / 1000 + end.tv_sec * 1000000) - (start.tv_nsec / 1000 + start.tv_sec * 1000000);
|
||||
if (tudiff < 1000000l / FPS) {
|
||||
usleep(1000000l / FPS - tudiff);
|
||||
}
|
||||
}
|
||||
|
||||
// Animation finished. Shut down the RGB matrix.
|
||||
canvas->Clear();
|
||||
delete canvas;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
|
||||
// Small example how to use the library.
|
||||
// For more examples, look at demo-main.cc
|
||||
//
|
||||
// This code is public domain
|
||||
// (but note, that the led-matrix library this depends on is GPL v2)
|
||||
|
||||
#include "led-matrix.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
|
||||
using rgb_matrix::RGBMatrix;
|
||||
using rgb_matrix::Canvas;
|
||||
|
||||
volatile bool interrupt_received = false;
|
||||
static void InterruptHandler(int signo) {
|
||||
interrupt_received = true;
|
||||
}
|
||||
|
||||
static void DrawOnCanvas(Canvas *canvas) {
|
||||
/*
|
||||
* Let's create a simple animation. We use the canvas to draw
|
||||
* pixels. We wait between each step to have a slower animation.
|
||||
*/
|
||||
canvas->Fill(0, 0, 255);
|
||||
|
||||
int center_x = canvas->width() / 2;
|
||||
int center_y = canvas->height() / 2;
|
||||
float radius_max = canvas->width() / 2;
|
||||
float angle_step = 1.0 / 360;
|
||||
for (float a = 0, r = 0; r < radius_max; a += angle_step, r += angle_step) {
|
||||
if (interrupt_received)
|
||||
return;
|
||||
float dot_x = cos(a * 2 * M_PI) * r;
|
||||
float dot_y = sin(a * 2 * M_PI) * r;
|
||||
canvas->SetPixel(center_x + dot_x, center_y + dot_y,
|
||||
255, 0, 0);
|
||||
usleep(1 * 1000); // wait a little to slow down things.
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
RGBMatrix::Options defaults;
|
||||
defaults.hardware_mapping = "regular"; // or e.g. "adafruit-hat"
|
||||
defaults.rows = 32;
|
||||
defaults.chain_length = 1;
|
||||
defaults.parallel = 1;
|
||||
defaults.show_refresh_rate = true;
|
||||
Canvas *canvas = RGBMatrix::CreateFromFlags(&argc, &argv, &defaults);
|
||||
if (canvas == NULL)
|
||||
return 1;
|
||||
|
||||
// It is always good to set up a signal handler to cleanly exit when we
|
||||
// receive a CTRL-C for instance. The DrawOnCanvas() routine is looking
|
||||
// for that.
|
||||
signal(SIGTERM, InterruptHandler);
|
||||
signal(SIGINT, InterruptHandler);
|
||||
|
||||
DrawOnCanvas(canvas); // Using the canvas.
|
||||
|
||||
// Animation finished. Shut down the RGB matrix.
|
||||
canvas->Clear();
|
||||
delete canvas;
|
||||
|
||||
return 0;
|
||||
}
|
||||
210
depends/rpi-rgb-led-matrix/examples-api-use/pixel-mover.cc
Normal file
210
depends/rpi-rgb-led-matrix/examples-api-use/pixel-mover.cc
Normal file
@@ -0,0 +1,210 @@
|
||||
// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
|
||||
// Small example how to use the input bits
|
||||
//
|
||||
// This code is public domain
|
||||
// (but note, that the led-matrix library this depends on is GPL v2)
|
||||
|
||||
#include "led-matrix.h"
|
||||
#include "graphics.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <getopt.h>
|
||||
#include <math.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <deque>
|
||||
|
||||
using namespace rgb_matrix;
|
||||
|
||||
volatile bool interrupt_received = false;
|
||||
static void InterruptHandler(int signo) {
|
||||
interrupt_received = true;
|
||||
}
|
||||
|
||||
static void InteractiveUseMessage() {
|
||||
fprintf(stderr,
|
||||
"Move around with common movement keysets \n"
|
||||
" W,A,S,D (gaming move style) or\n"
|
||||
" H,J,K,L (vi console move style)\n"
|
||||
" Quit with 'q' or <ESC>\n"
|
||||
"The pixel position cannot be less than 0 or greater than the "
|
||||
"display height and width.\n");
|
||||
}
|
||||
|
||||
static int usage(const char *progname) {
|
||||
fprintf(stderr, "usage: %s [options]\n", progname);
|
||||
fprintf(stderr, "Display single pixel with any colour.\n");
|
||||
InteractiveUseMessage();
|
||||
fprintf(stderr, "Options:\n\n");
|
||||
fprintf(stderr,
|
||||
"\t-C <r,g,b> : Color at front of trail. Default 255,255,0\n"
|
||||
"\t-t <trail-len> : Length of trail behind dot (default:0)\n"
|
||||
"\t-c <r,g,b> : Color at end of trail. Default 0,0,255\n\n"
|
||||
);
|
||||
rgb_matrix::PrintMatrixFlags(stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool parseColor(Color *c, const char *str) {
|
||||
return sscanf(str, "%hhu,%hhu,%hhu", &c->r, &c->g, &c->b) == 3;
|
||||
}
|
||||
|
||||
static char getch() {
|
||||
static bool is_terminal = isatty(STDIN_FILENO);
|
||||
|
||||
struct termios old;
|
||||
if (is_terminal) {
|
||||
if (tcgetattr(0, &old) < 0)
|
||||
perror("tcsetattr()");
|
||||
|
||||
// Set to unbuffered mode
|
||||
struct termios no_echo = old;
|
||||
no_echo.c_lflag &= ~ICANON;
|
||||
no_echo.c_lflag &= ~ECHO;
|
||||
no_echo.c_cc[VMIN] = 1;
|
||||
no_echo.c_cc[VTIME] = 0;
|
||||
if (tcsetattr(0, TCSANOW, &no_echo) < 0)
|
||||
perror("tcsetattr ICANON");
|
||||
}
|
||||
|
||||
char buf = 0;
|
||||
if (read(STDIN_FILENO, &buf, 1) < 0)
|
||||
perror ("read()");
|
||||
|
||||
if (is_terminal) {
|
||||
// Back to original terminal settings.
|
||||
if (tcsetattr(0, TCSADRAIN, &old) < 0)
|
||||
perror ("tcsetattr ~ICANON");
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
// Interpolation of color between head and tail of trail.
|
||||
static uint8_t quantize(float c) {
|
||||
return c < 0 ? 0 : c > 255 ? 255 : roundf(c);
|
||||
}
|
||||
static Color interpolate(const Color &c1, const Color &c2, float fraction) {
|
||||
float c2_fraction = 1 - fraction;
|
||||
return { quantize(c1.r * fraction + c2.r * c2_fraction),
|
||||
quantize(c1.g * fraction + c2.g * c2_fraction),
|
||||
quantize(c1.b * fraction + c2.b * c2_fraction)};
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
RGBMatrix::Options matrix_options;
|
||||
rgb_matrix::RuntimeOptions runtime_opt;
|
||||
if (!rgb_matrix::ParseOptionsFromFlags(&argc, &argv,
|
||||
&matrix_options, &runtime_opt)) {
|
||||
return usage(argv[0]);
|
||||
}
|
||||
|
||||
Color front_color(255, 255, 0);
|
||||
Color back_color(0, 0, 255);
|
||||
|
||||
int trail_len = 0;
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "C:c:t:")) != -1) {
|
||||
switch (opt) {
|
||||
case 't':
|
||||
trail_len = std::max(0, atoi(optarg));
|
||||
break;
|
||||
case 'C':
|
||||
if (!parseColor(&front_color, optarg)) {
|
||||
fprintf(stderr, "Invalid color spec: %s\n", optarg);
|
||||
return usage(argv[0]);
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
if (!parseColor(&back_color, optarg)) {
|
||||
fprintf(stderr, "Invalid color spec: %s\n", optarg);
|
||||
return usage(argv[0]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return usage(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
RGBMatrix *matrix = RGBMatrix::CreateFromOptions(matrix_options, runtime_opt);
|
||||
if (matrix == NULL)
|
||||
return usage(argv[0]);
|
||||
|
||||
rgb_matrix::FrameCanvas *canvas = matrix->CreateFrameCanvas();
|
||||
signal(SIGTERM, InterruptHandler);
|
||||
signal(SIGINT, InterruptHandler);
|
||||
|
||||
std::deque<std::pair<int, int>> trail;
|
||||
int x_pos = 0;
|
||||
int y_pos = 0;
|
||||
trail.push_back({x_pos, y_pos});
|
||||
|
||||
InteractiveUseMessage();
|
||||
const bool output_is_terminal = isatty(STDOUT_FILENO);
|
||||
|
||||
bool running = true;
|
||||
while (!interrupt_received && running) {
|
||||
canvas->Clear();
|
||||
int distance_from_head = trail.size();
|
||||
for (const auto &pos : trail) { // Draw from tail -> head
|
||||
distance_from_head--;
|
||||
Color c = interpolate(front_color, back_color,
|
||||
1.0 - 1.0f * distance_from_head / trail.size());
|
||||
canvas->SetPixel(pos.first, pos.second, c.r, c.g, c.b);
|
||||
}
|
||||
canvas = matrix->SwapOnVSync(canvas);
|
||||
|
||||
printf("%sX,Y = %d,%d%s",
|
||||
output_is_terminal ? "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" : "",
|
||||
x_pos, y_pos,
|
||||
output_is_terminal ? " " : "\n");
|
||||
fflush(stdout);
|
||||
|
||||
const char c = tolower(getch());
|
||||
switch (c) {
|
||||
case 'w': case 'k': // Up
|
||||
if (y_pos > 0) {
|
||||
y_pos--;
|
||||
trail.push_back({x_pos, y_pos});
|
||||
}
|
||||
break;
|
||||
case 's': case 'j': // Down
|
||||
if (y_pos < canvas->height() - 1) {
|
||||
y_pos++;
|
||||
trail.push_back({x_pos, y_pos});
|
||||
}
|
||||
break;
|
||||
case 'a': case 'h': // Left
|
||||
if (x_pos > 0) {
|
||||
x_pos--;
|
||||
trail.push_back({x_pos, y_pos});
|
||||
}
|
||||
break;
|
||||
case 'd': case 'l': // Right
|
||||
if (x_pos < canvas->width() - 1) {
|
||||
x_pos++;
|
||||
trail.push_back({x_pos, y_pos});
|
||||
}
|
||||
break;
|
||||
// All kinds of conditions which we use to exit
|
||||
case 0x1B: // Escape
|
||||
case 'q': // 'Q'uit
|
||||
case 0x04: // End of file
|
||||
case 0x00: // Other issue from getch()
|
||||
running = false;
|
||||
break;
|
||||
}
|
||||
|
||||
while ((int)trail.size() > trail_len + 1)
|
||||
trail.pop_front(); // items on front are the oldest dots
|
||||
}
|
||||
|
||||
// Finished. Shut down the RGB matrix.
|
||||
delete matrix;
|
||||
printf("\n");
|
||||
return 0;
|
||||
}
|
||||
BIN
depends/rpi-rgb-led-matrix/examples-api-use/runtext.ppm
Normal file
BIN
depends/rpi-rgb-led-matrix/examples-api-use/runtext.ppm
Normal file
Binary file not shown.
BIN
depends/rpi-rgb-led-matrix/examples-api-use/runtext16.ppm
Normal file
BIN
depends/rpi-rgb-led-matrix/examples-api-use/runtext16.ppm
Normal file
Binary file not shown.
@@ -0,0 +1,184 @@
|
||||
// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
|
||||
// Small example how to scroll text.
|
||||
//
|
||||
// This code is public domain
|
||||
// (but note, that the led-matrix library this depends on is GPL v2)
|
||||
|
||||
// For a utility with a few more features see
|
||||
// ../utils/text-scroller.cc
|
||||
|
||||
#include "led-matrix.h"
|
||||
#include "graphics.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <getopt.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace rgb_matrix;
|
||||
|
||||
volatile bool interrupt_received = false;
|
||||
static void InterruptHandler(int signo) {
|
||||
interrupt_received = true;
|
||||
}
|
||||
|
||||
static int usage(const char *progname) {
|
||||
fprintf(stderr, "usage: %s [options] <text>\n", progname);
|
||||
fprintf(stderr, "Takes text and scrolls it with speed -s\n");
|
||||
fprintf(stderr, "Options:\n");
|
||||
fprintf(stderr,
|
||||
"\t-s <speed> : Approximate letters per second. "
|
||||
"(Zero for no scrolling)\n"
|
||||
"\t-l <loop-count> : Number of loops through the text. "
|
||||
"-1 for endless (default)\n"
|
||||
"\t-f <font-file> : Use given font.\n"
|
||||
"\t-x <x-origin> : X-Origin of displaying text (Default: 0)\n"
|
||||
"\t-y <y-origin> : Y-Origin of displaying text (Default: 0)\n"
|
||||
"\t-t <track=spacing>: Spacing pixels between letters (Default: 0)\n"
|
||||
"\n"
|
||||
"\t-C <r,g,b> : Text-Color. Default 255,255,0\n"
|
||||
"\t-B <r,g,b> : Background-Color. Default 0,0,0\n"
|
||||
"\n"
|
||||
);
|
||||
rgb_matrix::PrintMatrixFlags(stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool parseColor(Color *c, const char *str) {
|
||||
return sscanf(str, "%hhu,%hhu,%hhu", &c->r, &c->g, &c->b) == 3;
|
||||
}
|
||||
|
||||
static bool FullSaturation(const Color &c) {
|
||||
return (c.r == 0 || c.r == 255)
|
||||
&& (c.g == 0 || c.g == 255)
|
||||
&& (c.b == 0 || c.b == 255);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
RGBMatrix::Options matrix_options;
|
||||
rgb_matrix::RuntimeOptions runtime_opt;
|
||||
if (!rgb_matrix::ParseOptionsFromFlags(&argc, &argv,
|
||||
&matrix_options, &runtime_opt)) {
|
||||
return usage(argv[0]);
|
||||
}
|
||||
|
||||
Color color(255, 255, 0);
|
||||
Color bg_color(0, 0, 0);
|
||||
|
||||
const char *bdf_font_file = NULL;
|
||||
std::string line;
|
||||
/* x_origin is set by default just right of the screen */
|
||||
const int x_default_start = (matrix_options.chain_length
|
||||
* matrix_options.cols) + 5;
|
||||
int x_orig = x_default_start;
|
||||
int y_orig = 0;
|
||||
int letter_spacing = 0;
|
||||
float speed = 7.0f;
|
||||
int loops = -1;
|
||||
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "x:y:f:C:B:t:s:l:")) != -1) {
|
||||
switch (opt) {
|
||||
case 's': speed = atof(optarg); break;
|
||||
case 'l': loops = atoi(optarg); break;
|
||||
case 'x': x_orig = atoi(optarg); break;
|
||||
case 'y': y_orig = atoi(optarg); break;
|
||||
case 'f': bdf_font_file = strdup(optarg); break;
|
||||
case 't': letter_spacing = atoi(optarg); break;
|
||||
case 'C':
|
||||
if (!parseColor(&color, optarg)) {
|
||||
fprintf(stderr, "Invalid color spec: %s\n", optarg);
|
||||
return usage(argv[0]);
|
||||
}
|
||||
break;
|
||||
case 'B':
|
||||
if (!parseColor(&bg_color, optarg)) {
|
||||
fprintf(stderr, "Invalid background color spec: %s\n", optarg);
|
||||
return usage(argv[0]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return usage(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = optind; i < argc; ++i) {
|
||||
line.append(argv[i]).append(" ");
|
||||
}
|
||||
|
||||
if (line.empty()) {
|
||||
fprintf(stderr, "Add the text you want to print on the command-line.\n");
|
||||
return usage(argv[0]);
|
||||
}
|
||||
|
||||
if (bdf_font_file == NULL) {
|
||||
fprintf(stderr, "Need to specify BDF font-file with -f\n");
|
||||
return usage(argv[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Load font. This needs to be a filename with a bdf bitmap font.
|
||||
*/
|
||||
rgb_matrix::Font font;
|
||||
if (!font.LoadFont(bdf_font_file)) {
|
||||
fprintf(stderr, "Couldn't load font '%s'\n", bdf_font_file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
RGBMatrix *canvas = RGBMatrix::CreateFromOptions(matrix_options, runtime_opt);
|
||||
if (canvas == NULL)
|
||||
return 1;
|
||||
|
||||
const bool all_extreme_colors = (matrix_options.brightness == 100)
|
||||
&& FullSaturation(color)
|
||||
&& FullSaturation(bg_color);
|
||||
if (all_extreme_colors)
|
||||
canvas->SetPWMBits(1);
|
||||
|
||||
signal(SIGTERM, InterruptHandler);
|
||||
signal(SIGINT, InterruptHandler);
|
||||
|
||||
printf("CTRL-C for exit.\n");
|
||||
|
||||
// Create a new canvas to be used with led_matrix_swap_on_vsync
|
||||
FrameCanvas *offscreen_canvas = canvas->CreateFrameCanvas();
|
||||
|
||||
int delay_speed_usec = 1000000;
|
||||
if (speed > 0) {
|
||||
delay_speed_usec = 1000000 / speed / font.CharacterWidth('W');
|
||||
} else if (x_orig == x_default_start) {
|
||||
// There would be no scrolling, so text would never appear. Move to front.
|
||||
x_orig = 0;
|
||||
}
|
||||
|
||||
int x = x_orig;
|
||||
int y = y_orig;
|
||||
int length = 0;
|
||||
|
||||
while (!interrupt_received && loops != 0) {
|
||||
offscreen_canvas->Fill(bg_color.r, bg_color.g, bg_color.b);
|
||||
// length = holds how many pixels our text takes up
|
||||
length = rgb_matrix::DrawText(offscreen_canvas, font,
|
||||
x, y + font.baseline(),
|
||||
color, nullptr,
|
||||
line.c_str(), letter_spacing);
|
||||
|
||||
if (speed > 0 && --x + length < 0) {
|
||||
x = x_orig;
|
||||
if (loops > 0) --loops;
|
||||
}
|
||||
|
||||
// Swap the offscreen_canvas with canvas on vsync, avoids flickering
|
||||
offscreen_canvas = canvas->SwapOnVSync(offscreen_canvas);
|
||||
usleep(delay_speed_usec);
|
||||
}
|
||||
|
||||
// Finished. Shut down the RGB matrix.
|
||||
delete canvas;
|
||||
|
||||
return 0;
|
||||
}
|
||||
179
depends/rpi-rgb-led-matrix/examples-api-use/text-example.cc
Normal file
179
depends/rpi-rgb-led-matrix/examples-api-use/text-example.cc
Normal file
@@ -0,0 +1,179 @@
|
||||
// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
|
||||
// Small example how write text.
|
||||
//
|
||||
// This code is public domain
|
||||
// (but note, that the led-matrix library this depends on is GPL v2)
|
||||
|
||||
#include "led-matrix.h"
|
||||
#include "graphics.h"
|
||||
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace rgb_matrix;
|
||||
|
||||
static int usage(const char *progname) {
|
||||
fprintf(stderr, "usage: %s [options]\n", progname);
|
||||
fprintf(stderr, "Reads text from stdin and displays it. "
|
||||
"Empty string: clear screen\n");
|
||||
fprintf(stderr, "Options:\n");
|
||||
fprintf(stderr,
|
||||
"\t-f <font-file> : Use given font.\n"
|
||||
"\t-x <x-origin> : X-Origin of displaying text (Default: 0)\n"
|
||||
"\t-y <y-origin> : Y-Origin of displaying text (Default: 0)\n"
|
||||
"\t-S <spacing> : Spacing pixels between letters (Default: 0)\n"
|
||||
"\t-C <r,g,b> : Color. Default 255,255,0\n"
|
||||
"\t-B <r,g,b> : Font Background-Color. Default 0,0,0\n"
|
||||
"\t-O <r,g,b> : Outline-Color, e.g. to increase contrast.\n"
|
||||
"\t-F <r,g,b> : Panel flooding-background color. Default 0,0,0\n"
|
||||
"\n"
|
||||
);
|
||||
rgb_matrix::PrintMatrixFlags(stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool parseColor(Color *c, const char *str) {
|
||||
return sscanf(str, "%hhu,%hhu,%hhu", &c->r, &c->g, &c->b) == 3;
|
||||
}
|
||||
|
||||
static bool FullSaturation(const Color &c) {
|
||||
return (c.r == 0 || c.r == 255)
|
||||
&& (c.g == 0 || c.g == 255)
|
||||
&& (c.b == 0 || c.b == 255);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
RGBMatrix::Options matrix_options;
|
||||
rgb_matrix::RuntimeOptions runtime_opt;
|
||||
if (!rgb_matrix::ParseOptionsFromFlags(&argc, &argv,
|
||||
&matrix_options, &runtime_opt)) {
|
||||
return usage(argv[0]);
|
||||
}
|
||||
|
||||
Color color(255, 255, 0);
|
||||
Color bg_color(0, 0, 0);
|
||||
Color flood_color(0, 0, 0);
|
||||
Color outline_color(0,0,0);
|
||||
bool with_outline = false;
|
||||
|
||||
const char *bdf_font_file = NULL;
|
||||
int x_orig = 0;
|
||||
int y_orig = 0;
|
||||
int letter_spacing = 0;
|
||||
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "x:y:f:C:B:O:S:F:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'x': x_orig = atoi(optarg); break;
|
||||
case 'y': y_orig = atoi(optarg); break;
|
||||
case 'f': bdf_font_file = strdup(optarg); break;
|
||||
case 'S': letter_spacing = atoi(optarg); break;
|
||||
case 'C':
|
||||
if (!parseColor(&color, optarg)) {
|
||||
fprintf(stderr, "Invalid color spec: %s\n", optarg);
|
||||
return usage(argv[0]);
|
||||
}
|
||||
break;
|
||||
case 'B':
|
||||
if (!parseColor(&bg_color, optarg)) {
|
||||
fprintf(stderr, "Invalid background color spec: %s\n", optarg);
|
||||
return usage(argv[0]);
|
||||
}
|
||||
break;
|
||||
case 'O':
|
||||
if (!parseColor(&outline_color, optarg)) {
|
||||
fprintf(stderr, "Invalid outline color spec: %s\n", optarg);
|
||||
return usage(argv[0]);
|
||||
}
|
||||
with_outline = true;
|
||||
break;
|
||||
case 'F':
|
||||
if (!parseColor(&flood_color, optarg)) {
|
||||
fprintf(stderr, "Invalid background color spec: %s\n", optarg);
|
||||
return usage(argv[0]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return usage(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (bdf_font_file == NULL) {
|
||||
fprintf(stderr, "Need to specify BDF font-file with -f\n");
|
||||
return usage(argv[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Load font. This needs to be a filename with a bdf bitmap font.
|
||||
*/
|
||||
rgb_matrix::Font font;
|
||||
if (!font.LoadFont(bdf_font_file)) {
|
||||
fprintf(stderr, "Couldn't load font '%s'\n", bdf_font_file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we want an outline around the font, we create a new font with
|
||||
* the original font as a template that is just an outline font.
|
||||
*/
|
||||
rgb_matrix::Font *outline_font = NULL;
|
||||
if (with_outline) {
|
||||
outline_font = font.CreateOutlineFont();
|
||||
}
|
||||
|
||||
RGBMatrix *canvas = RGBMatrix::CreateFromOptions(matrix_options, runtime_opt);
|
||||
if (canvas == NULL)
|
||||
return 1;
|
||||
|
||||
const bool all_extreme_colors = (matrix_options.brightness == 100)
|
||||
&& FullSaturation(color)
|
||||
&& FullSaturation(bg_color)
|
||||
&& FullSaturation(outline_color);
|
||||
if (all_extreme_colors)
|
||||
canvas->SetPWMBits(1);
|
||||
|
||||
const int x = x_orig;
|
||||
int y = y_orig;
|
||||
|
||||
if (isatty(STDIN_FILENO)) {
|
||||
// Only give a message if we are interactive. If connected via pipe, be quiet
|
||||
printf("Enter lines. Full screen or empty line clears screen.\n"
|
||||
"Supports UTF-8. CTRL-D for exit.\n");
|
||||
}
|
||||
|
||||
canvas->Fill(flood_color.r, flood_color.g, flood_color.b);
|
||||
char line[1024];
|
||||
while (fgets(line, sizeof(line), stdin)) {
|
||||
const size_t last = strlen(line);
|
||||
if (last > 0) line[last - 1] = '\0'; // remove newline.
|
||||
bool line_empty = strlen(line) == 0;
|
||||
if ((y + font.height() > canvas->height()) || line_empty) {
|
||||
canvas->Fill(flood_color.r, flood_color.g, flood_color.b);
|
||||
y = y_orig;
|
||||
}
|
||||
if (line_empty)
|
||||
continue;
|
||||
if (outline_font) {
|
||||
// The outline font, we need to write with a negative (-2) text-spacing,
|
||||
// as we want to have the same letter pitch as the regular text that
|
||||
// we then write on top.
|
||||
rgb_matrix::DrawText(canvas, *outline_font,
|
||||
x - 1, y + font.baseline(),
|
||||
outline_color, &bg_color, line, letter_spacing - 2);
|
||||
}
|
||||
// The regular text. Unless we already have filled the background with
|
||||
// the outline font, we also fill the background here.
|
||||
rgb_matrix::DrawText(canvas, font, x, y + font.baseline(),
|
||||
color, outline_font ? NULL : &bg_color, line,
|
||||
letter_spacing);
|
||||
y += font.height();
|
||||
}
|
||||
|
||||
// Finished. Shut down the RGB matrix.
|
||||
delete canvas;
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user