You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
5.7 KiB
C++
211 lines
5.7 KiB
C++
// -*- 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;
|
|
}
|