o Initial parsing of matrix command line options.

This commit is contained in:
Henner Zeller 2016-08-20 17:25:06 -07:00
parent eb0a10b0f0
commit edce0acadb
8 changed files with 281 additions and 125 deletions

View file

@ -3,7 +3,8 @@
// This code is public domain
// (but note, once linked against the led-matrix library, this is
// covered by the GPL v2)
//
// This is a grab-bag of various demos and not very readable.
#include "led-matrix.h"
#include "threaded-canvas-manipulator.h"
#include "transformer.h"
@ -21,6 +22,9 @@
using std::min;
using std::max;
#define TERM_ERR "\033[1;31m"
#define TERM_NORM "\033[0m"
using namespace rgb_matrix;
/*
@ -1012,26 +1016,22 @@ private:
static int usage(const char *progname) {
fprintf(stderr, "usage: %s <options> -D <demo-nr> [optional parameter]\n",
progname);
fprintf(stderr, "Options:\n"
"\t-r <rows> : Panel rows. '16' for 16x32 (1:8 multiplexing),\n"
"\t '32' for 32x32 (1:16), '8' for 1:4 multiplexing; "
"64 for 1:32 multiplexing. "
"Default: 32\n"
"\t-P <parallel> : For Plus-models or RPi2: parallel chains. 1..3. "
"Default: 1\n"
"\t-c <chained> : Daisy-chained boards. Default: 1.\n"
"\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-l : Don't do luminance correction (CIE1931)\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, "
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. "
"\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");
fprintf(stderr, "Demos, choosen with -D\n");
fprintf(stderr, "\t0 - some rotating square\n"
@ -1066,6 +1066,11 @@ int main(int argc, char *argv[]) {
const char *demo_parameter = NULL;
// 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, "dlD:t:r:P:c:p:b:m:LR:")) != -1) {
switch (opt) {
@ -1081,18 +1086,6 @@ int main(int argc, char *argv[]) {
runtime_seconds = atoi(optarg);
break;
case 'r':
options.rows = atoi(optarg);
break;
case 'P':
options.parallel = atoi(optarg);
break;
case 'c':
options.chain_length = atoi(optarg);
break;
case 'm':
scroll_ms = atoi(optarg);
break;
@ -1120,6 +1113,28 @@ int main(int argc, char *argv[]) {
rotation = atoi(optarg);
break;
// These used to be options we understood, but deprecate now. Accept them
// for now, but tell the 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);
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);
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);
break;
default: /* '?' */
return usage(argv[0]);
}
@ -1129,44 +1144,32 @@ int main(int argc, char *argv[]) {
demo_parameter = argv[optind];
}
std::string err;
if (!options.Validate(&err)) {
fprintf(stderr, "%s", err.c_str());
return 1;
}
if (demo < 0) {
fprintf(stderr, "Expected required option -D <demo>\n");
fprintf(stderr, TERM_ERR "Expected required option -D <demo>\n" TERM_NORM);
return usage(argv[0]);
}
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 1;
}
if (options.rows != 8 && options.rows != 16
&& options.rows != 32 && options.rows != 64) {
fprintf(stderr, "Rows can one of 8, 16, 32 or 64 "
"for 1:4, 1:8, 1:16 and 1:32 multiplexing respectively.\n");
return 1;
}
if (options.chain_length < 1) {
fprintf(stderr, "Chain outside usable range\n");
return 1;
}
if (options.chain_length > 8) {
fprintf(stderr, "That is a long chain. Expect some flicker.\n");
}
if (options.parallel < 1 || options.parallel > 3) {
fprintf(stderr, "Parallel outside usable range.\n");
return 1;
}
if (brightness < 1 || brightness > 100) {
fprintf(stderr, "Brightness is outside usable range.\n");
fprintf(stderr, TERM_ERR "Brightness is outside usable range.\n" TERM_NORM);
return 1;
}
if (rotation % 90 != 0) {
fprintf(stderr, "Rotation %d not allowed! "
"Only 0, 90, 180 and 270 are possible.\n", rotation);
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;
}

View file

@ -47,9 +47,15 @@ int main(int argc, char *argv[]) {
* Set up the RGBMatrix. It implements a 'Canvas' interface.
*/
RGBMatrix::Options options;
options.rows = 32; // A 32x32 display. Use 16 when this is a 16x32 display.
options.chain_length = 1; // Number of boards chained together.
options.parallel = 1; // Number of chains in parallel (1..3).
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.

View file

@ -19,17 +19,14 @@ 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"
"\t-f <font-file>: Use given font.\n"
"\t-r <rows> : Display rows. 16 for 16x32, 32 for 32x32. "
"Default: 32\n"
"\t-P <parallel> : For Plus-models or RPi2: parallel chains. 1..3. "
"Default: 1\n"
"\t-c <chained> : Daisy-chained boards. Default: 1.\n"
"\t-b <brightness>: Sets brightness percent. Default: 100.\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-C <r,g,b> : Color. Default 255,255,0\n");
fprintf(stderr, "Options:\n");
RGBMatrix::Options::FlagUsageMessage();
fprintf(stderr,
"\t-f <font-file> : Use given font.\n"
"\t-b <brightness> : Sets brightness percent. Default: 100.\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-C <r,g,b> : Color. Default 255,255,0\n");
return 1;
}
@ -45,12 +42,14 @@ int main(int argc, char *argv[]) {
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, "r:P:c:x:y:f:C:b:")) != -1) {
while ((opt = getopt(argc, argv, "x:y:f:C:b:")) != -1) {
switch (opt) {
case 'r': options.rows = atoi(optarg); break;
case 'P': options.parallel = atoi(optarg); break;
case 'c': options.chain_length = atoi(optarg); break;
case 'b': brightness = atoi(optarg); break;
case 'x': x_orig = atoi(optarg); break;
case 'y': y_orig = atoi(optarg); break;
@ -80,24 +79,12 @@ int main(int argc, char *argv[]) {
return usage(argv[0]);
}
if (options.rows != 8 && options.rows != 16
&& options.rows != 32 && options.rows != 64) {
fprintf(stderr, "Rows can one of 8, 16, 32 or 64 "
"for 1:4, 1:8, 1:16 and 1:32 multiplexing respectively.\n");
std::string err;
if (!options.Validate(&err)) {
fprintf(stderr, "%s", err.c_str());
return 1;
}
if (options.chain_length < 1) {
fprintf(stderr, "Chain outside usable range\n");
return 1;
}
if (options.chain_length > 8) {
fprintf(stderr, "That is a long chain. Expect some flicker.\n");
}
if (options.parallel < 1 || options.parallel > 3) {
fprintf(stderr, "Parallel outside usable range.\n");
return 1;
}
if (brightness < 1 || brightness > 100) {
fprintf(stderr, "Brightness is outside usable range.\n");
return 1;

View file

@ -21,6 +21,7 @@
#define RPI_RGBMATRIX_H
#include <stdint.h>
#include <string>
#include <vector>
#include "gpio.h"
@ -50,6 +51,23 @@ public:
struct Options {
Options(); // Creates a default option set.
// Validate the options and possibly output a message to
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;

View file

@ -3,7 +3,8 @@
# So
# -lrgbmatrix
##
OBJECTS=gpio.o led-matrix.o framebuffer.o thread.o bdf-font.o graphics.o transformer.o led-matrix-c.o
OBJECTS=gpio.o led-matrix.o options-initialize.o framebuffer.o \
thread.o bdf-font.o graphics.o transformer.o led-matrix-c.o
TARGET=librgbmatrix
###

View file

@ -119,7 +119,7 @@ private:
unsigned requested_frame_multiple_;
};
// Some defaults.
// Some defaults. See options-initialize.cc for the command line parsing.
RGBMatrix::Options::Options() : rows(32), chain_length(1), parallel(1) {}
RGBMatrix::RGBMatrix(GPIO *io, const Options &options)

128
lib/options-initialize.cc Normal file
View file

@ -0,0 +1,128 @@
// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
// Copyright (C) 2013, 2016 Henner Zeller <h.zeller@acm.org>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// 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 <vector>
namespace rgb_matrix {
namespace {
typedef char** argv_iterator;
static bool ConsumeIntFlag(const char *flag_name,
argv_iterator &pos, const argv_iterator end,
int *result_value, int *error) {
const char *option = *pos;
const size_t flag_len = strlen(flag_name);
if (strncmp(option, flag_name, flag_len) != 0)
return false; // not consumed.
const char *value;
if (option[flag_len] == '=') // --option=42 # value in same arg
value = option + flag_len + 1;
else if (pos + 1 < end) { // --option 42 # value in next arg
value = *(++pos);
} else {
fprintf(stderr, "Parameter expected after %s\n", flag_name);
++*error;
return true; // consumed, but error.
}
char *end_value = NULL;
int val = strtol(value, &end_value, 10);
if (!*value || *end_value) {
fprintf(stderr, "Couldn't parse parameter %s=%s "
"(Expected number but '%s' looks funny)\n",
flag_name, value, end_value);
++*error;
return true; // consumed, but error
}
*result_value = val;
return true; // consumed.
}
static bool OptFlagInit(int &argc, char **&argv, RGBMatrix::Options *mopts) {
argv_iterator it = &argv[0];
argv_iterator end = it + argc;
std::vector<char*> unused_options;
unused_options.push_back(*it++); // Not interested in program name
int err = 0;
bool posix_end_option_seen = false;
for (/**/; it < end; ++it) {
posix_end_option_seen |= (strcmp(*it, "--") == 0);
if (!posix_end_option_seen) {
if (ConsumeIntFlag("--led-rows", it, end, &mopts->rows, &err))
continue;
if (ConsumeIntFlag("--led-chain", it, end, &mopts->chain_length, &err))
continue;
if (ConsumeIntFlag("--led-parallel", it, end, &mopts->parallel, &err))
continue;
}
unused_options.push_back(*it);
}
if (err > 0) {
return false;
}
// Success. Re-arrange flags to only include the ones not consumed.
argc = (int) unused_options.size();
for (int i = 0; i < argc; ++i) {
argv[i] = unused_options[i];
}
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);
}
void RGBMatrix::Options::FlagUsageMessage() {
fprintf(stderr,
"\t--led-rows <rows> : Panel rows. 8, 16, 32 or 64. "
"Default: 32\n"
"\t--led-parallel <parallel> : For Plus-models or RPi2: parallel "
"chains. 1..3. Default: 1\n"
"\t--led-chain <chained> : Number of daisy-chained boards. "
"Default: 1.\n");
}
bool RGBMatrix::Options::Validate(std::string *err) {
bool any_error = false;
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;
}
if (chain_length < 1) {
err->append("Chain-length outside usable range\n");
any_error = true;
}
if (parallel < 1 || parallel > 3) {
err->append("Parallel outside usable range.\n");
any_error = true;
}
return !any_error;
}
} // namespace rgb_matrix

View file

@ -43,6 +43,9 @@ static void InterruptHandler(int signo) {
interrupt_received = true;
}
#define TERM_ERR "\033[1;31m"
#define TERM_NORM "\033[0m"
namespace {
// Preprocess as much as possible, so that we can just exchange full frames
// on VSync.
@ -147,16 +150,14 @@ 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"
"\t-r <rows> : Panel rows. '16' for 16x32 (1:8 multiplexing),\n"
"\t '32' for 32x32 (1:16), '8' for 1:4 multiplexing; 64 for 1:32 multiplexing."
"Default: 32\n"
"\t-P <parallel> : For Plus-models or RPi2: parallel chains. 1..3. "
"Default: 1\n"
"\t-c <chained> : Daisy-chained boards. Default: 1.\n"
"\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");
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");
return 1;
}
@ -169,12 +170,14 @@ int main(int argc, char *argv[]) {
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 'r': options.rows = atoi(optarg); break;
case 'P': options.parallel = atoi(optarg); break;
case 'c': options.chain_length = atoi(optarg); break;
case 'p': pwm_bits = atoi(optarg); break;
case 'd': as_daemon = true; break;
case 'b': brightness = atoi(optarg); break;
@ -183,30 +186,40 @@ int main(int argc, char *argv[]) {
options.rows = 32;
large_display = true;
break;
// These used to be options we understood, but deprecate now. Accept them
// for now, but tell the 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);
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);
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);
break;
default:
return usage(argv[0]);
}
}
if (options.rows != 8 && options.rows != 16
&& options.rows != 32 && options.rows != 64) {
fprintf(stderr, "Rows can one of 8, 16, 32 or 64 "
"for 1:4, 1:8, 1:16 and 1:32 multiplexing respectively.\n");
std::string err;
if (!options.Validate(&err)) {
fprintf(stderr, "%s", err.c_str());
return 1;
}
if (options.chain_length < 1) {
fprintf(stderr, "Chain outside usable range\n");
return usage(argv[0]);
}
if (options.chain_length > 8) {
fprintf(stderr, "That is a long chain. Expect some flicker.\n");
}
if (options.parallel < 1 || options.parallel > 3) {
fprintf(stderr, "Parallel outside usable range.\n");
return usage(argv[0]);
}
if (brightness < 1 || brightness > 100) {
fprintf(stderr, "Brightness is outside usable range.\n");
return usage(argv[0]);