o Add some common runtim options (--led-daemon, --led-drop-privs)

o Add more options for matrix parameters (--led-brightness, --led-pwm-bits)
This commit is contained in:
Henner Zeller 2016-08-20 19:26:20 -07:00
parent edce0acadb
commit 7557c40e17
8 changed files with 239 additions and 267 deletions

View file

@ -25,6 +25,8 @@ $(RGB_LIBRARY): FORCE
clean:
$(MAKE) -C lib clean
$(MAKE) -C examples-api-use clean
$(MAKE) -C utils clean
$(MAKE) -C $(PYTHON_LIB_DIR) clean
build-python: $(RGB_LIBRARY)

View file

@ -1017,22 +1017,12 @@ static int usage(const char *progname) {
fprintf(stderr, "usage: %s <options> -D <demo-nr> [optional parameter]\n",
progname);
fprintf(stderr, "Options:\n");
RGBMatrix::Options::FlagUsageMessage();
fprintf(stderr,
"\t-L : 'Large' display, composed out of 4 times 32x32\n"
"\t-p <pwm-bits> : Bits used for PWM. Something between 1..11\n"
"\t-D <demo-nr> : Always needs to be set\n"
"\t-d : run as daemon. Use this when starting "
"in\n"
"\t /etc/init.d, but also when "
"running without\n"
"\t terminal (e.g. cron).\n"
"\t-t <seconds> : Run for these number of seconds, then exit.\n"
"\t (if neither -d nor -t are supplied, "
"waits for <RETURN>)\n"
"\t-b <brightnes> : Sets brightness percent. Default: 100.\n"
"\t-R <rotation> : Sets the rotation of matrix. "
"Allowed: 0, 90, 180, 270. Default: 0.\n");
rgb_matrix::PrintMatrixOptions(stderr);
fprintf(stderr, "Demos, choosen with -D\n");
fprintf(stderr, "\t0 - some rotating square\n"
"\t1 - forward scrolling an image (-m <scroll-ms>)\n"
@ -1052,36 +1042,23 @@ static int usage(const char *progname) {
}
int main(int argc, char *argv[]) {
GPIO io;
bool as_daemon = false;
// First things first: create matrix and take command line flags.
RGBMatrix *matrix = CreateMatrixFromFlags(&argc, &argv);
int runtime_seconds = -1;
int demo = -1;
RGBMatrix::Options options;
int scroll_ms = 30;
int pwm_bits = -1;
int brightness = 100;
int rotation = 0;
bool large_display = false;
bool do_luminance_correct = true;
const char *demo_parameter = NULL;
// First, let's consume the flags for the options.
if (!options.InitializeFromFlags(&argc, &argv)) {
return usage(argv[0]);
}
bool any_deprecated_option = false;
int opt;
while ((opt = getopt(argc, argv, "dlD:t:r:P:c:p:b:m:LR:")) != -1) {
while ((opt = getopt(argc, argv, "dD:t:r:P:c:p:b:m:LR:")) != -1) {
switch (opt) {
case 'D':
demo = atoi(optarg);
break;
case 'd':
as_daemon = true;
break;
case 't':
runtime_seconds = atoi(optarg);
break;
@ -1090,49 +1067,39 @@ int main(int argc, char *argv[]) {
scroll_ms = atoi(optarg);
break;
case 'p':
pwm_bits = atoi(optarg);
break;
case 'b':
brightness = atoi(optarg);
break;
case 'l':
do_luminance_correct = !do_luminance_correct;
break;
case 'L':
// The 'large' display assumes a chain of four displays with 32x32
options.chain_length = 4;
options.rows = 32;
large_display = true;
break;
case 'R':
rotation = atoi(optarg);
break;
// These used to be options we understood, but deprecate now. Accept them
// for now, but tell the user.
// These used to be options we understood, but deprecated now. Tell user.
case 'r':
options.rows = atoi(optarg);
fprintf(stderr, TERM_ERR "-r is a deprecated option. "
"Please use --led-rows=%d instead!\n" TERM_NORM, options.rows);
fprintf(stderr, "-r is a deprecated option. "
"Please use --led-rows=... instead!\n");
any_deprecated_option = true;
break;
case 'P':
options.parallel = atoi(optarg);
fprintf(stderr, TERM_ERR "-P is a deprecated option. "
"Please use --led-parallel=%d instead!\n" TERM_NORM,
options.parallel);
fprintf(stderr, "-P is a deprecated option. "
"Please use --led-parallel=... instead!\n");
any_deprecated_option = true;
break;
case 'c':
options.chain_length = atoi(optarg);
fprintf(stderr, TERM_ERR "-c is a deprecated option. "
"Please use --led-chain=%d instead!\n" TERM_NORM,
options.chain_length);
fprintf(stderr, "-c is a deprecated option. "
"Please use --led-chain=... instead!\n");
any_deprecated_option = true;
break;
case 'p':
fprintf(stderr, "-p is a deprecated option. "
"Please use --led-pwm-bits=... instead!\n");
any_deprecated_option = true;
break;
case 'b':
fprintf(stderr, "-b is a deprecated option. "
"Please use --led-brightness=... instead!\n");
any_deprecated_option = true;
break;
default: /* '?' */
@ -1140,69 +1107,31 @@ int main(int argc, char *argv[]) {
}
}
if (any_deprecated_option)
return usage(argv[0]);
if (optind < argc) {
demo_parameter = argv[optind];
}
std::string err;
if (!options.Validate(&err)) {
fprintf(stderr, "%s", err.c_str());
return 1;
}
if (demo < 0) {
fprintf(stderr, TERM_ERR "Expected required option -D <demo>\n" TERM_NORM);
return usage(argv[0]);
}
if (brightness < 1 || brightness > 100) {
fprintf(stderr, TERM_ERR "Brightness is outside usable range.\n" TERM_NORM);
return 1;
}
if (rotation % 90 != 0) {
fprintf(stderr, TERM_ERR "Rotation %d not allowed! "
"Only 0, 90, 180 and 270 are possible.\n" TERM_NORM, rotation);
return 1;
}
if (getuid() != 0) {
fprintf(stderr, TERM_ERR "Must run as root to be able to access /dev/mem\n"
"Prepend 'sudo' to the command:\n\tsudo %s ...\n" TERM_NORM,
argv[0]);
return 1;
}
// Initialize GPIO pins. This might fail when we don't have permissions.
if (!io.Init())
return 1;
// Start daemon before we start any threads.
if (as_daemon) {
if (fork() != 0)
return 0;
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
}
// The matrix, our 'frame buffer' and display updater.
RGBMatrix *matrix = new RGBMatrix(&io, options);
matrix->set_luminance_correct(do_luminance_correct);
matrix->SetBrightness(brightness);
if (pwm_bits >= 0 && !matrix->SetPWMBits(pwm_bits)) {
fprintf(stderr, "Invalid range of pwm-bits\n");
if (matrix == NULL)
return 1;
}
LinkedTransformer *transformer = new LinkedTransformer();
matrix->SetTransformer(transformer);
if (large_display) {
// Mapping the coordinates of a 32x128 display mapped to a square of 64x64
transformer->AddTransformer(new LargeSquare64x64Transformer());
}
if (rotation > 0) {
transformer->AddTransformer(new RotateTransformer(rotation));
}
@ -1278,9 +1207,7 @@ int main(int argc, char *argv[]) {
// Now, the image generation runs in the background. We can do arbitrary
// things here in parallel. In this demo, we're essentially just
// waiting for one of the conditions to exit.
if (as_daemon) {
sleep(runtime_seconds > 0 ? runtime_seconds : INT_MAX);
} else if (runtime_seconds > 0) {
if (runtime_seconds > 0) {
sleep(runtime_seconds);
} else {
// Things are set up. Just wait for <RETURN> to be pressed.

View file

@ -36,28 +36,10 @@ static void DrawOnCanvas(Canvas *canvas) {
}
int main(int argc, char *argv[]) {
/*
* Set up GPIO pins. This fails when not running as root.
*/
GPIO io;
if (!io.Init())
Canvas *canvas = rgb_matrix::CreateMatrixFromFlags(&argc, &argv);
if (canvas == NULL)
return 1;
/*
* Set up the RGBMatrix. It implements a 'Canvas' interface.
*/
RGBMatrix::Options options;
if (!options.InitializeFromFlags(&argc, &argv)) {
options.FlagUsageMessage();
return 1;
}
std::string err;
if (!options.Validate(&err)) {
fprintf(stderr, "%s", err.c_str());
return 1;
}
Canvas *canvas = new RGBMatrix(&io, options);
DrawOnCanvas(canvas); // Using the canvas.
// Animation finished. Shut down the RGB matrix.

View file

@ -20,7 +20,7 @@ static int usage(const char *progname) {
fprintf(stderr, "Reads text from stdin and displays it. "
"Empty string: clear screen\n");
fprintf(stderr, "Options:\n");
RGBMatrix::Options::FlagUsageMessage();
rgb_matrix::PrintMatrixOptions(stderr);
fprintf(stderr,
"\t-f <font-file> : Use given font.\n"
"\t-b <brightness> : Sets brightness percent. Default: 100.\n"
@ -35,18 +35,14 @@ static bool parseColor(Color *c, const char *str) {
}
int main(int argc, char *argv[]) {
RGBMatrix *canvas = rgb_matrix::CreateMatrixFromFlags(&argc, &argv);
Color color(255, 255, 0);
const char *bdf_font_file = NULL;
RGBMatrix::Options options;
int x_orig = 0;
int y_orig = -1;
int brightness = 100;
// First, let's consume the flags for the options.
if (!options.InitializeFromFlags(&argc, &argv)) {
return usage(argv[0]);
}
int opt;
while ((opt = getopt(argc, argv, "x:y:f:C:b:")) != -1) {
switch (opt) {
@ -65,6 +61,9 @@ int main(int argc, char *argv[]) {
}
}
if (canvas == NULL)
return 1;
if (bdf_font_file == NULL) {
fprintf(stderr, "Need to specify BDF font-file with -f\n");
return usage(argv[0]);
@ -79,28 +78,11 @@ int main(int argc, char *argv[]) {
return usage(argv[0]);
}
std::string err;
if (!options.Validate(&err)) {
fprintf(stderr, "%s", err.c_str());
return 1;
}
if (brightness < 1 || brightness > 100) {
fprintf(stderr, "Brightness is outside usable range.\n");
return 1;
}
/*
* Set up GPIO pins. This fails when not running as root.
*/
GPIO io;
if (!io.Init())
return 1;
/*
* Set up the RGBMatrix. It implements a 'Canvas' interface.
*/
RGBMatrix *canvas = new RGBMatrix(&io, options);
canvas->SetBrightness(brightness);
bool all_extreme_colors = brightness == 100;

View file

@ -30,9 +30,34 @@
#include "transformer.h"
namespace rgb_matrix {
class RGBMatrix;
class FrameCanvas; // Canvas for Double- and Multibuffering
namespace internal { class Framebuffer; }
// Convenience factory utility to create a Matrix and set values from the
// command line. You pass it a pointer to the argc and argv of main, it
// extracts the relevant options and leaves the remaining options.
//
// Example use:
/*
using rgb_matrix::RGBMatrix;
int main(int argc, char **argv) {
RGBMatrix *matrix = rgb_matrix::CreateMatrixFromFlags(&argc, &argv);
if (matrix == NULL) {
PrintMatrixOptions(stderr);
return 1;
}
// Do your own command line handling with the remaining options.
// .. now use matrix
delete matrix; // Make sure to delete it in the end.
}
*/
RGBMatrix *CreateMatrixFromFlags(int *argc, char ***argv);
void PrintMatrixOptions(FILE *out);
// The RGB matrix provides the framebuffer and the facilities to constantly
// update the LED matrix.
//
@ -51,23 +76,9 @@ public:
struct Options {
Options(); // Creates a default option set.
// Validate the options and possibly output a message to
// Validate the options and possibly output a message to string.
bool Validate(std::string *err);
// --led_rows, --led_chain, --led_parallel
// This modifies the argc and argv, so use in main such as
// int main(int argc, char *argv[]) {
// RGBMatrix::Options options;
// if (!options.InitializeFromFlags(&argc, &argv)) {
// return 1;
// }
// // ... now do the relevant stuff.
// }
bool InitializeFromFlags(int *argc, char ***argv);
// Usage message that explains the available flags.
static void FlagUsageMessage();
// The "rows" are the number
// of rows supported by the display, so 32 or 16. Default: 32.
int rows;
@ -81,6 +92,15 @@ public:
// also be 2 or 3. The effective number of pixels in vertical direction is
// then thus rows * parallel. Default: 1
int parallel;
// Set PWM bits used for output. Default is 11, but if you only deal with
// limited comic-colors, 1 might be sufficient. Lower require less CPU and
// increases refresh-rate.
int pwm_bits;
// The initial brightness of the panel in percent. Valid range is 1..100
// Default: 100
int brightness;
};
// Create an RGBMatrix.

View file

@ -120,11 +120,14 @@ private:
};
// Some defaults. See options-initialize.cc for the command line parsing.
RGBMatrix::Options::Options() : rows(32), chain_length(1), parallel(1) {}
RGBMatrix::Options::Options()
: rows(32), chain_length(1), parallel(1), pwm_bits(11), brightness(100) {}
RGBMatrix::RGBMatrix(GPIO *io, const Options &options)
: rows_(options.rows), chained_displays_(options.chain_length),
parallel_displays_(options.parallel),
pwm_bits_(options.pwm_bits),
brightness_(options.brightness),
io_(NULL), updater_(NULL) {
SetTransformer(NULL);
active_ = CreateFrameCanvas();

View file

@ -14,15 +14,31 @@
// along with this program. If not, see <http://gnu.org/licenses/gpl-2.0.txt>
#include "led-matrix.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <grp.h>
#include <pwd.h>
#include <vector>
namespace rgb_matrix {
namespace {
typedef char** argv_iterator;
static bool ConsumeBoolFlag(const char *flag_name, const argv_iterator &pos,
bool *result_value) {
const char *option = *pos;
const size_t flag_len = strlen(flag_name);
if (strncmp(option, flag_name, flag_len) != 0)
return false; // not consumed.
*result_value = !*result_value;
return true;
}
static bool ConsumeIntFlag(const char *flag_name,
argv_iterator &pos, const argv_iterator end,
int *result_value, int *error) {
@ -53,7 +69,16 @@ static bool ConsumeIntFlag(const char *flag_name,
return true; // consumed.
}
static bool OptFlagInit(int &argc, char **&argv, RGBMatrix::Options *mopts) {
struct RuntimeOptions {
RuntimeOptions() : as_daemon(false), drop_privileges(false) {}
bool as_daemon;
bool drop_privileges;
};
static bool FlagInit(int &argc, char **&argv,
RGBMatrix::Options *mopts,
RuntimeOptions *ropts) {
argv_iterator it = &argv[0];
argv_iterator end = it + argc;
@ -71,6 +96,14 @@ static bool OptFlagInit(int &argc, char **&argv, RGBMatrix::Options *mopts) {
continue;
if (ConsumeIntFlag("--led-parallel", it, end, &mopts->parallel, &err))
continue;
if (ConsumeIntFlag("--led-brightness", it, end, &mopts->brightness, &err))
continue;
if (ConsumeIntFlag("--led-pwm-bits", it, end, &mopts->pwm_bits, &err))
continue;
if (ConsumeBoolFlag("--led-daemon", it, &ropts->as_daemon))
continue;
if (ConsumeBoolFlag("--led-drop-privs", it, &ropts->drop_privileges))
continue;
}
unused_options.push_back(*it);
}
@ -86,43 +119,118 @@ static bool OptFlagInit(int &argc, char **&argv, RGBMatrix::Options *mopts) {
}
return true;
}
} // namespace
bool RGBMatrix::Options::InitializeFromFlags(int *argc, char ***argv) {
// Unfortunately, we can't use getopt_long(), as it does not provide a
// way to only fish out some of the flags and leave the rest as-is without
// much complaining. So we have to do this here ourselves.
return OptFlagInit(*argc, *argv, this);
static bool drop_privs(const char *priv_user, const char *priv_group) {
uid_t ruid, euid, suid;
if (getresuid(&ruid, &euid, &suid) >= 0) {
if (euid != 0) // not root anyway. No priv dropping.
return true;
}
void RGBMatrix::Options::FlagUsageMessage() {
fprintf(stderr,
"\t--led-rows <rows> : Panel rows. 8, 16, 32 or 64. "
struct group *g = getgrnam(priv_group);
if (g == NULL) {
perror("group lookup.");
return false;
}
if (setresgid(g->gr_gid, g->gr_gid, g->gr_gid) != 0) {
perror("setresgid()");
return false;
}
struct passwd *p = getpwnam(priv_user);
if (p == NULL) {
perror("user lookup.");
return false;
}
if (setresuid(p->pw_uid, p->pw_uid, p->pw_uid) != 0) {
perror("setresuid()");
return false;
}
return true;
}
} // namespace
// Public interface.
RGBMatrix *CreateMatrixFromFlags(int *argc, char ***argv) {
RGBMatrix::Options mopt;
RuntimeOptions ropt;
if (!FlagInit(*argc, *argv, &mopt, &ropt)) {
return NULL;
}
std::string error;
if (!mopt.Validate(&error)) {
fprintf(stderr, "%s\n", error.c_str());
return NULL;
}
if (getuid() != 0) {
fprintf(stderr, "Must run as root to be able to access /dev/mem\n"
"Prepend 'sudo' to the command:\n\tsudo %s ...\n", (*argv)[0]);
return NULL;
}
static GPIO io; // This static var is a little bit icky.
if (!io.Init()) {
return NULL;
}
if (ropt.as_daemon && daemon(1, 0) != 0) {
perror("Failed to become daemon");
}
RGBMatrix *result = new RGBMatrix(&io, mopt);
if (ropt.drop_privileges) {
drop_privs("daemon", "daemon");
}
return result;
}
void PrintMatrixOptions(FILE *out) {
fprintf(out,
"\t--led-rows=<rows> : Panel rows. 8, 16, 32 or 64. "
"Default: 32\n"
"\t--led-parallel <parallel> : For Plus-models or RPi2: parallel "
"\t--led-chain=<chained> : Number of daisy-chained panels. "
"Default: 1.\n"
"\t--led-parallel=<parallel> : For A/B+ models or RPi2,3b: parallel "
"chains. 1..3. Default: 1\n"
"\t--led-chain <chained> : Number of daisy-chained boards. "
"Default: 1.\n");
"\t--led-pwm-bits=<1..11> : PWM bits. Default: 11\n"
"\t--led-brightness=<percent>: Brightness in percent. Default: 100.\n"
"\t--led-drop-privs : Drop privileges from 'root'.\n"
"\t--led-daemon : Make the process run as daemon.\n");
}
bool RGBMatrix::Options::Validate(std::string *err) {
bool any_error = false;
bool success = true;
if (rows != 8 && rows != 16 && rows != 32 && rows != 64) {
err->append("Invalid number or panel rows. "
"Should be one of 8, 16, 32 or 64\n");
any_error = true;
success = false;
}
if (chain_length < 1) {
err->append("Chain-length outside usable range\n");
any_error = true;
success = false;
}
if (parallel < 1 || parallel > 3) {
err->append("Parallel outside usable range.\n");
any_error = true;
success = false;
}
return !any_error;
if (brightness < 1 || brightness > 100) {
err->append("Brightness is outside usable range.\n");
success = false;
}
if (pwm_bits <= 0 || pwm_bits > 11) {
err->append("Invalid range of pwm-bits\n");
success = false;
}
return success;
}
} // namespace rgb_matrix

View file

@ -151,62 +151,47 @@ static void DisplayAnimation(const std::vector<PreprocessedFrame*> &frames,
static int usage(const char *progname) {
fprintf(stderr, "usage: %s [options] <image>\n", progname);
fprintf(stderr, "Options:\n");
RGBMatrix::Options::FlagUsageMessage();
fprintf(stderr,
"\t-L : Large 64x64 display made "
"from four 32x32 in a chain\n"
"\t-d : Run as daemon.\n"
"\t-b <brightnes> : Sets brightness percent. "
"Default: 100.\n");
rgb_matrix::PrintMatrixOptions(stderr);
return 1;
}
int main(int argc, char *argv[]) {
Magick::InitializeMagick(*argv);
RGBMatrix::Options options;
int pwm_bits = -1;
int brightness = 100;
bool large_display = false; // example for using Transformers
bool as_daemon = false;
// First, let's consume the flags for the options.
if (!options.InitializeFromFlags(&argc, &argv)) {
return usage(argv[0]);
}
int opt;
while ((opt = getopt(argc, argv, "r:P:c:p:b:dL")) != -1) {
switch (opt) {
case 'p': pwm_bits = atoi(optarg); break;
case 'd': as_daemon = true; break;
case 'b': brightness = atoi(optarg); break;
case 'L':
options.chain_length = 4;
options.rows = 32;
large_display = true;
break;
RGBMatrix *const matrix = rgb_matrix::CreateMatrixFromFlags(&argc, &argv);
// These used to be options we understood, but deprecate now. Accept them
// for now, but tell the user.
bool any_deprecated_option = false;
int opt;
while ((opt = getopt(argc, argv, "r:P:c:p:b:d")) != -1) {
switch (opt) {
case 'r':
options.rows = atoi(optarg);
fprintf(stderr, TERM_ERR "-r is a deprecated option. "
"Please use --led-rows=%d instead!\n" TERM_NORM, options.rows);
fprintf(stderr, "-r is a deprecated option. "
"Please use --led-rows=... instead!\n");
any_deprecated_option = true;
break;
case 'P':
options.parallel = atoi(optarg);
fprintf(stderr, TERM_ERR "-P is a deprecated option. "
"Please use --led-parallel=%d instead!\n" TERM_NORM,
options.parallel);
fprintf(stderr, "-P is a deprecated option. "
"Please use --led-parallel=... instead!\n");
any_deprecated_option = true;
break;
case 'c':
options.chain_length = atoi(optarg);
fprintf(stderr, TERM_ERR "-c is a deprecated option. "
"Please use --led-chain=%d instead!\n" TERM_NORM,
options.chain_length);
fprintf(stderr, "-c is a deprecated option. "
"Please use --led-chain=... instead!\n");
any_deprecated_option = true;
break;
case 'p':
fprintf(stderr, "-p is a deprecated option. "
"Please use --led-pwm-bits=... instead!\n");
any_deprecated_option = true;
break;
case 'b':
fprintf(stderr, "-b is a deprecated option. "
"Please use --led-brightness=... instead!\n");
any_deprecated_option = true;
break;
default:
@ -214,56 +199,19 @@ int main(int argc, char *argv[]) {
}
}
std::string err;
if (!options.Validate(&err)) {
fprintf(stderr, "%s", err.c_str());
return 1;
}
if (brightness < 1 || brightness > 100) {
fprintf(stderr, "Brightness is outside usable range.\n");
if (any_deprecated_option)
return usage(argv[0]);
}
if (optind >= argc) {
fprintf(stderr, "Expected image filename.\n");
return usage(argv[0]);
}
if (matrix == NULL)
return 1;
const char *filename = argv[optind];
/*
* Set up GPIO pins. This fails when not running as root.
*/
GPIO io;
if (!io.Init())
return 1;
// Start daemon before we start any threads.
if (as_daemon) {
if (fork() != 0)
return 0;
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
}
RGBMatrix *const matrix = new RGBMatrix(&io, options);
if (pwm_bits >= 0 && !matrix->SetPWMBits(pwm_bits)) {
fprintf(stderr, "Invalid range of pwm-bits\n");
return 1;
}
matrix->SetBrightness(brightness);
// Here is an example where to add your own transformer. In this case, we
// just to the chain-of-four-32x32 => 64x64 transformer, but just use any
// of the transformers in transformer.h or write your own.
if (large_display) {
// Mapping the coordinates of a 32x128 display mapped to a square of 64x64
matrix->SetTransformer(new rgb_matrix::LargeSquare64x64Transformer());
}
std::vector<Magick::Image> sequence_pics;
if (!LoadAnimation(filename, matrix->width(), matrix->height(),
&sequence_pics)) {