-
Notifications
You must be signed in to change notification settings - Fork 31
/
Copy pathrpi-fb-matrix.cpp
198 lines (180 loc) · 6.81 KB
/
rpi-fb-matrix.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
// Program to copy the contents of the Raspberry Pi primary display to LED matrices.
// Author: Tony DiCola
#include <iostream>
#include <stdexcept>
#include <bcm_host.h>
#include <fcntl.h>
#include <led-matrix.h>
#include <linux/fb.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#include "Config.h"
#include "GridTransformer.h"
using namespace std;
using namespace rgb_matrix;
// Global to keep track of if the program should run.
// Will be set false by a SIGINT handler when ctrl-c is
// pressed, then the main loop will cleanly exit.
volatile bool running = true;
// Class to encapsulate all the logic for capturing an image of the Pi's primary
// display. Manages all the BCM GPU and CPU resources automatically while in scope.
class BCMDisplayCapture {
public:
BCMDisplayCapture(int width=-1, int height=-1):
_width(width),
_height(height),
_display(0),
_screen_resource(0),
_screen_data(NULL)
{
// Get information about primary/HDMI display.
_display = vc_dispmanx_display_open(0);
if (!_display) {
throw runtime_error("Unable to open primary display!");
}
DISPMANX_MODEINFO_T display_info;
if (vc_dispmanx_display_get_info(_display, &display_info)) {
throw runtime_error("Unable to get primary display information!");
}
cout << "Primary display:" << endl
<< " resolution: " << display_info.width << "x" << display_info.height << endl
<< " format: " << display_info.input_format << endl;
// If no width and height were specified then grab the entire screen.
if ((_width == -1) || (_height == -1)) {
_width = display_info.width;
_height = display_info.height;
}
// Create a GPU image surface to hold the captured screen.
uint32_t image_prt;
_screen_resource = vc_dispmanx_resource_create(VC_IMAGE_RGB888, _width, _height, &image_prt);
if (!_screen_resource) {
throw runtime_error("Unable to create screen surface!");
}
// Create a rectangular region of the captured screen size.
vc_dispmanx_rect_set(&_rect, 0, 0, _width, _height);
// Allocate CPU memory for copying out the captured screen. Must be aligned
// to a larger size because of GPU surface memory size constraints.
_pitch = ALIGN_UP(_width*3, 32);
_screen_data = new uint8_t[_pitch*_height];
}
void capture() {
// Capture the primary display and copy it from GPU to CPU memory.
vc_dispmanx_snapshot(_display, _screen_resource, (DISPMANX_TRANSFORM_T)0);
vc_dispmanx_resource_read_data(_screen_resource, &_rect, _screen_data, _pitch);
}
void getPixel(int x, int y, uint8_t* r, uint8_t* g, uint8_t* b) {
// Grab the requested pixel from the last captured display image.
uint8_t* row = _screen_data + (y*_pitch);
*r = row[x*3];
*g = row[x*3+1];
*b = row[x*3+2];
}
~BCMDisplayCapture() {
// Clean up BCM and other resources.
if (_screen_resource != 0) {
vc_dispmanx_resource_delete(_screen_resource);
}
if (_display != 0) {
vc_dispmanx_display_close(_display);
}
if (_screen_data != NULL) {
delete[] _screen_data;
}
}
private:
int _width,
_height,
_pitch;
DISPMANX_DISPLAY_HANDLE_T _display;
DISPMANX_RESOURCE_HANDLE_T _screen_resource;
VC_RECT_T _rect;
uint8_t* _screen_data;
};
static void sigintHandler(int s) {
running = false;
}
static void usage(const char* progname) {
std::cerr << "Usage: " << progname << " [flags] [config-file]" << std::endl;
std::cerr << "Flags:" << std::endl;
rgb_matrix::RGBMatrix::Options matrix_options;
rgb_matrix::RuntimeOptions runtime_options;
runtime_options.drop_privileges = -1; // Need root
rgb_matrix::PrintMatrixFlags(stderr, matrix_options, runtime_options);
}
int main(int argc, char** argv) {
try {
// Initialize from flags.
rgb_matrix::RGBMatrix::Options matrix_options;
rgb_matrix::RuntimeOptions runtime_options;
runtime_options.drop_privileges = -1; // Need root
if (!rgb_matrix::ParseOptionsFromFlags(&argc, &argv,
&matrix_options, &runtime_options)) {
usage(argv[0]);
return 1;
}
// Read additional configuration from config file if it exists
Config config(&matrix_options, argc >= 2 ? argv[1] : "/dev/null");
cout << "Using config values: " << endl
<< " display_width: " << config.getDisplayWidth() << endl
<< " display_height: " << config.getDisplayHeight() << endl
<< " panel_width: " << config.getPanelWidth() << endl
<< " panel_height: " << config.getPanelHeight() << endl
<< " chain_length: " << config.getChainLength() << endl
<< " parallel_count: " << config.getParallelCount() << endl;
// Set screen capture state depending on if a crop region is specified or not.
// When not cropped grab the entire screen and resize it down to the LED display.
// However when cropping is enabled instead grab the entire screen (by
// setting the capture_width and capture_height to -1) and specify an offset
// to the start of the crop rectangle.
int capture_width = config.getDisplayWidth();
int capture_height = config.getDisplayHeight();
int x_offset = 0;
int y_offset = 0;
if (config.hasCropOrigin()) {
cout << " crop_origin: (" << config.getCropX() << ", " << config.getCropY() << ")" << endl;
capture_width = -1;
capture_height = -1;
x_offset = config.getCropX();
y_offset = config.getCropY();
}
// Initialize matrix library.
// Create canvas and apply GridTransformer.
RGBMatrix *canvas = CreateMatrixFromOptions(matrix_options, runtime_options);
if (config.hasTransformer()) {
canvas->ApplyStaticTransformer(config.getGridTransformer());
}
canvas->Clear();
// Initialize BCM functions and display capture class.
bcm_host_init();
BCMDisplayCapture displayCapture(capture_width, capture_height);
// Loop forever waiting for Ctrl-C signal to quit.
signal(SIGINT, sigintHandler);
cout << "Press Ctrl-C to quit..." << endl;
while (running) {
// Capture the current display image.
displayCapture.capture();
// Loop through the frame data and set the pixels on the matrix canvas.
for (int y=0; y<config.getDisplayHeight(); ++y) {
for (int x=0; x<config.getDisplayWidth(); ++x) {
uint8_t red, green, blue;
displayCapture.getPixel(x+x_offset, y+y_offset, &red, &green, &blue);
canvas->SetPixel(x, y, red, green, blue);
}
}
// Sleep for 25 milliseconds (40Hz refresh)
usleep(25 * 1000);
}
canvas->Clear();
delete canvas;
}
catch (const exception& ex) {
cerr << ex.what() << endl;
usage(argv[0]);
return -1;
}
return 0;
}