--- /dev/null
+/*
+ * SDL-based Knight-Rider Kitt style effect
+ * Prototype to port to AVR+TCL. For this reason levels are 0-4095.
+ * Copyright (c) 2021, Matthew Mondor
+ *
+ * Example usage:
+ * $ gmake (or make if your OS uses GNUMake)
+ * $ ./kitt
+ * $ ./kitt -n16 -w64 -h64 -p4 -T4 -t7 -d450
+ * $ ./kitt -n16 -w92 -h48 -p4 -t3 -T4 -t8 -d255 -l 2048 -r 0 -g 0 -b 255
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <SDL.h>
+#include <SDL_framerate.h>
+
+
+#define SCREEN_DEPTH 32
+
+#define FPS 60 /* 0 = unrestricted */
+#define SHOWFPS false
+#define FULLSCREEN false
+
+
+int main(int, char **);
+void usage(void);
+
+static void err(const char *);
+static void warn(const char *);
+static bool screen_init(int, int);
+static void screen_destroy(void);
+
+inline static uint8_t color_scale(uint8_t, int);
+static void draw_screen(void);
+static void modulate(void);
+static void tlc(int, int);
+
+
+static SDL_Surface *screen_surface = NULL;
+
+/* Used for frame rate delay */
+#if (FPS > 0)
+static FPSmanager fpsm;
+#endif
+
+/* Simulate set/getprogname(3) on GNU */
+static const char *progname = "";
+
+/* Modulator state */
+static int *levels = NULL;
+static int channel = 0;
+static int direction = 1;
+
+/* Parameters */
+static int slots = 6, slot_width = 144, slot_height = 64;
+static int color_r = 0xff, color_g = 0x20, color_b = 0x20;
+static int bright_min = 1024, bright_max = 4095, tail = 4, head = 2,
+ dim = 896, spause = 8;
+
+
+/* ARGSUSED */
+int
+main(int argc, char **argv)
+{
+ int ch, i, skip;
+
+ progname = strdup(argv[0]);
+ while ((ch = getopt(argc, argv, "?n:w:h:l:L:T:t:d:p:r:g:b:")) != -1) {
+ switch (ch) {
+ case 'n':
+ slots = atoi(optarg);
+ break;
+ case 'w':
+ slot_width = atoi(optarg);
+ break;
+ case 'h':
+ slot_height = atoi(optarg);
+ break;
+ case 'l':
+ bright_min = atoi(optarg);
+ break;
+ case 'L':
+ bright_max = atoi(optarg);
+ break;
+ case 'T':
+ head = atoi(optarg);
+ break;
+ case 't':
+ tail = atoi(optarg);
+ break;
+ case 'd':
+ dim = atoi(optarg);
+ break;
+ case 'p':
+ spause = atoi(optarg);
+ break;
+ case 'r':
+ color_r = atoi(optarg);
+ break;
+ case 'g':
+ color_g = atoi(optarg);
+ break;
+ case 'b':
+ color_b = atoi(optarg);
+ break;
+ case '?': /* FALLTHROUGH */
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!screen_init(slot_width * slots, slot_height))
+ err("screen_init()");
+
+ /* Modulator initialization */
+ if ((levels = malloc(sizeof(int) * slots)) == NULL)
+ err("malloc()");
+ channel = slots / 2;
+ skip = spause;
+ for (i = 0; i < slots; i++)
+ tlc(i, bright_min);
+
+ for (;;) {
+ SDL_Event ev;
+
+ if (SDL_PollEvent(&ev)) {
+ switch (ev.type) {
+ case SDL_MOUSEBUTTONDOWN:
+ case SDL_KEYDOWN:
+ case SDL_QUIT:
+ goto end;
+ }
+ }
+
+ draw_screen();
+ if (--skip < 1) {
+ modulate();
+ skip = spause;
+ }
+ }
+
+end:
+ if (levels != NULL)
+ free(levels);
+
+ exit(EXIT_SUCCESS);
+}
+
+void
+usage(void)
+{
+
+ (void) fprintf(stderr,
+ "Usage: %s [-n <slots>] [-w <slotwidth>] [-h <slotheight>] "
+ "[-L <brightmax>] [-l <brightmin>] [-T <head>] [-t <tail>] "
+ "[-d <dim>] [-r <red>] [-g <green>] [-b <blue>] "
+ "[-p <pause>]\n", progname);
+ exit(EXIT_FAILURE);
+}
+
+static void
+err(const char *str)
+{
+
+ (void) fprintf(stderr, "%s\n", str);
+ exit(EXIT_FAILURE);
+}
+
+static void
+warn(const char *str)
+{
+
+ (void) fprintf(stdout, "%s\n", str);
+}
+
+static bool
+screen_init(int width, int height)
+{
+ const SDL_VideoInfo *vi;
+ int screen_flags = 0;
+
+ if (SDL_Init(SDL_INIT_VIDEO) == -1)
+ return false;
+
+ if ((vi = SDL_GetVideoInfo()) == NULL)
+ return false;
+ screen_flags |= SDL_HWPALETTE | SDL_DOUBLEBUF;
+#if (FULLSCREEN == true)
+ screen_flags |= SDL_FULLSCREEN;
+#endif
+ if (vi->hw_available)
+ screen_flags |= SDL_HWSURFACE;
+ else {
+ warn("Note: Hardware surfaces not available.");
+ screen_flags |= SDL_SWSURFACE;
+ }
+ if (vi->blit_hw)
+ screen_flags |= SDL_HWACCEL;
+ else
+ warn("Note: Hardware blitting not available.");
+
+ if ((screen_surface = SDL_SetVideoMode(width, height,
+ SCREEN_DEPTH, screen_flags)) == NULL)
+ return false;
+
+ /* Initialize FPS delay */
+#if (FPS > 0)
+ SDL_initFramerate(&fpsm);
+ if (SDL_setFramerate(&fpsm, FPS) == -1)
+ return false;
+#endif
+
+ (void) atexit(screen_destroy);
+ return true;
+}
+
+static void
+screen_destroy(void)
+{
+
+ SDL_Quit();
+}
+
+/*
+ * Level is 0-4095 vs color, 0-255 (16x)
+ */
+inline static uint8_t
+color_scale(uint8_t col, int level)
+{
+
+ if (level == 0)
+ return 0;
+ /* level / 4096 = ? / col */
+ return (level * col / 4096);
+}
+
+static void
+draw_screen(void)
+{
+ int i;
+ SDL_Rect rect;
+
+#if (SHOWFPS == true)
+ static Uint32 fps_cnt = 0, fps = 0;
+#endif
+
+ rect.x = rect.y = 0;
+ rect.w = slot_width;
+ rect.h = slot_height;
+ for (i = 0; i < slots; i++, rect.x += slot_width) {
+ uint8_t r, g, b;
+ uint32_t c;
+ int l = levels[i];
+
+ /* Scale level */
+ r = color_scale(color_r, l);
+ g = color_scale(color_g, l);
+ b = color_scale(color_b, l);
+ /* Compose RGB color */
+ c = (((r << 16) & 0x00ff0000) | ((g << 8) & 0x0000ff00) | \
+ (b & 0x000000ff));
+
+ (void)SDL_FillRect(screen_surface, &rect, c);
+ }
+
+ (void)SDL_Flip(screen_surface);
+
+ /* Delay as necessary to maintain frame rate */
+#if (FPS > 0)
+ SDL_framerateDelay(&fpsm);
+#endif
+#if (SHOWFPS == true)
+ /* Evaluate FPS */
+ fps++;
+ {
+ Uint32 t = SDL_GetTicks();
+
+ if (t - fps_cnt >= 5000) {
+ float seconds = (t - fps_cnt) / 1000.0;
+ float fps2 = fps / seconds;
+
+ printf("%d frames in %g seconds = %g FPS\n",
+ fps, seconds, fps2);
+ fps_cnt = t;
+ fps = 0;
+ }
+ }
+#endif
+}
+
+void
+modulate(void)
+{
+ int i, c, b;
+
+ for (i = 0; i < slots; i++)
+ tlc(i, bright_min);
+
+ for (i = 0, b = bright_max, c = channel;
+ i < head;
+ i++, b -= dim, c += direction)
+ tlc(c, b);
+ for (i = 0, b = bright_max, c = channel;
+ i < tail;
+ i++, b -= dim, c -= direction)
+ tlc(c, b);
+
+ c = channel + direction;
+ if (c < 0 || c == slots)
+ direction = -direction;
+ else
+ channel = c;
+}
+
+void
+tlc(int c, int l)
+{
+ if (c < 0 || c >= slots)
+ return;
+ assert(l >= 0 && l < 4096);
+
+ levels[c] = l;
+// printf("%02d = %04d\n", c, l);
+}