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.
199 lines
5.8 KiB
C++
199 lines
5.8 KiB
C++
1 year ago
|
// -*- 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;
|
||
|
}
|