1
0
mirror of git://projects.qi-hardware.com/ben-blinkenlights.git synced 2024-11-05 16:33:09 +02:00
ben-blinkenlights/ubb-la/gui.c

413 lines
8.3 KiB
C

/*
* gui.c - Graphical output for ubb-la
*
* Written 2013 by Werner Almesberger
* Copyright 2013 Werner Almesberger
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE /* for vasprintf */
#endif
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include "SDL.h"
#include "SDL_gfxPrimitives.h"
#include "SDL_gfxPrimitives_font.h"
#include "gui.h"
#if 0
#define DEBUG(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
#else
#define DEBUG(fmt, ...)
#endif
#define XRES 320 /* canvas width */
#define YRES 240 /* canvas height */
#define TEXT_RGBA 0xffffffff /* general text */
#define MAP_BUF_RGBA 0x808080ff /* buffer in the map */
#define MAP_VIEW_RGBA 0xffffffff /* current view in the map */
#define LEVEL_RGBA 0xffff00ff /* constant level or single change */
#define BOUNCE_RGBA 0xff8080ff /* bouncing signal */
#define LABEL_RGBA 0xffffffff /* channel label */
#define XCENTER ((XRES+CH_XOFF)/2)
#define MAP_BUF_Y0 2
#define MAP_BUF_Y1 8
#define MAP_VIEW_Y0 0
#define MAP_VIEW_Y1 10
#define CH_XOFF 30
#define CH_YOFF 30
#define CH_SKIP 16
#define CH_HEIGHT 8
#define MAX_ZOOM 3
#define FREQ_X 0
#define FREQ_Y 220
#define INTERVAL_X 0
#define INTERVAL_Y 230
#define REPEAT_DELAY_MS 300
#define REPEAT_INTERVAL_MS 30
static SDL_Surface *surf;
/* ----- SDL initialization and screen-wide functions ---------------------- */
void gui_init(void)
{
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
fprintf(stderr, "SDL_init: %s\n", SDL_GetError());
exit(1);
}
atexit(SDL_Quit);
surf = SDL_SetVideoMode(XRES, YRES, 0, SDL_SWSURFACE);
if (!surf) {
fprintf(stderr, "SDL_SetVideoMode: %s\n", SDL_GetError());
exit(1);
}
SDL_EnableKeyRepeat(REPEAT_DELAY_MS, REPEAT_INTERVAL_MS);
}
static void clear(void)
{
SDL_LockSurface(surf);
SDL_FillRect(surf, NULL, SDL_MapRGB(surf->format, 0, 0, 0));
}
static void update(void)
{
SDL_UnlockSurface(surf);
SDL_UpdateRect(surf, 0, 0, 0, 0);
}
/* ----- Text output ------------------------------------------------------- */
/*
* stringColor from SDL_gfx fails for some reason. SDL_ttf is just too much
* compatibility trouble. So we do our own.
*/
static void textf(int x, int y, uint32_t color, const char *fmt, ...)
{
va_list ap;
char *s;
uint8_t *p;
int ix, iy;
int res;
va_start(ap, fmt);
res = vasprintf(&s, fmt, ap);
va_end(ap);
(void) res;
while (*s) {
p = gfxPrimitivesFontdata+(*s << 3);
for (iy = 0; iy != 8; iy++) {
for (ix = 0; ix != 8; ix++)
if ((*p << ix) & 0x80)
pixelColor(surf, x+ix, y+iy, color);
p++;
}
x += 8;
s++;
}
}
/* ----- Map of buffer and view -------------------------------------------- */
static void show_map(int skip, int nibbles, int s0, int s1)
{
int w = nibbles-skip;
int m = (nibbles+skip) >> 1;
int scale = 0;
while (w >= (XRES-CH_XOFF)/2) {
w >>= 1;
scale++;
}
boxColor(surf, XCENTER-(w >> 1), MAP_BUF_Y0,
XCENTER+(w >> 1), MAP_BUF_Y1, MAP_BUF_RGBA);
rectangleColor(surf, (s0-m+(XCENTER << scale)) >> scale, MAP_VIEW_Y0,
(s1-m+(XCENTER << scale)) >> scale, MAP_VIEW_Y1, MAP_VIEW_RGBA);
}
/* ----- Waveform elements ------------------------------------------------- */
static inline int ch_y(int ch, int v)
{
return CH_YOFF+CH_SKIP*ch+(v ? 0 : CH_HEIGHT);
}
static void bounce(int x, int ch)
{
vlineColor(surf, x, ch_y(ch, 0), ch_y(ch, 1), BOUNCE_RGBA);
}
static void change(int x, int ch)
{
vlineColor(surf, x, ch_y(ch, 0), ch_y(ch, 1), LEVEL_RGBA);
}
static void level(int x0, int x1, int ch, int v)
{
if (x0 == x1)
pixelColor(surf, x0, ch_y(ch, v), LEVEL_RGBA);
else
hlineColor(surf, x0, x1, ch_y(ch, v), LEVEL_RGBA);
}
/* ----- Show (part of) a buffer ------------------------------------------- */
static void show_buffer_zoom_in(const uint8_t *buf, int skip, int nibbles,
int x0, int x1)
{
int f = (x1-x0)/(nibbles-skip);
int x, i, j;
int last[4] = { -1, -1, -1, -1};
uint8_t v, bit;
DEBUG("in: %d-%d (%d) Sa %d-%d (%d) pix %d f (%d)\n",
skip, nibbles, nibbles-skip, x0, x1, x1-x0, f, f*(nibbles-skip));
x = x0;
for (i = skip; i != nibbles; i++) {
v = (buf[i >> 1] >> 4*(~i & 1)) & 0xf;
for (j = 0; j != 4; j++) {
bit = (v >> j) & 1;
if (bit == last[j] || last[j] == -1) {
level(x, x+f-1, j, bit);
} else {
change(x, j);
level(x+1, x+f-1, j, bit);
}
last[j] = bit;
}
x += f;
}
}
static void show_buffer_zoom_out(const uint8_t *buf, int skip, int nibbles,
int x0, int x1)
{
int f = (nibbles-skip)/(x1-x0);
int x;
int i = skip, n, j;
int changes[4], last[4] = { -1, -1, -1, -1}, next[4];
uint8_t v, bit;
DEBUG("out: %d-%d (%d) Sa %d-%d (%d) pix %d f (%d)\n",
skip, nibbles, nibbles-skip, x0, x1, x1-x0, f, f*(x1-x0));
for (x = x0; x != x1; x++) {
n = i+f;
for (j = 0; j != 4; j++) {
next[j] = last[j];
changes[j] = 0;
}
while (i != n) {
v = (buf[i >> 1] >> 4*(~i & 1)) & 0xf;
for (j = 0; j != 4; j++) {
bit = (v >> j) & 1;
if (bit != next[j]) {
changes[j]++;
next[j] = bit;
}
}
i++;
}
for (j = 0; j != 4; j++) {
if (changes[j] > 1)
bounce(x, j);
else if (!changes[j] || last[j] == -1)
level(x, x, j, next[j]);
else
change(x, j);
last[j] = next[j];
}
}
}
static void show_buffer(const uint8_t *buf, int skip, int nibbles,
int x0, int x1, int zoom, int pos)
{
int xm, w, s, p0, p1;
int d, dp;
xm = (x0+x1) >> 1;
dp = pos-((nibbles+skip) >> 1);
if (zoom < 0) {
s = (x1-x0) << -zoom;
show_map(skip, nibbles, pos-s/2, pos+s/2);
w = (nibbles-skip) >> -zoom;
p0 = xm-(w >> 1)-(dp >> -zoom);
p1 = xm+((w+1) >> 1)-(dp >> -zoom);
if (p0 < x0) {
skip += (x0-p0) << -zoom;
p0 = x0;
}
if (p1 > x1) {
nibbles -= (p1-x1) << -zoom;
p1 = x1;
}
show_buffer_zoom_out(buf, skip, nibbles, p0, p1);
} else {
s = (x1-x0) >> zoom;
show_map(skip, nibbles, pos-s/2, pos+s/2);
w = (nibbles-skip) << zoom;
p0 = xm-(w >> 1)-(dp << zoom);
p1 = xm+((w+1) >> 1)-(dp << zoom);
if (p0 < x0) {
d = ((x0-p0)+(1 << zoom)-1) >> zoom;
skip += d;
p0 += d << zoom;
}
if (p1 > x1) {
d = ((p1-x1)+(1 << zoom)-1) >> zoom;
nibbles -= d;
p1 -= d << zoom;
}
show_buffer_zoom_in(buf, skip, nibbles, p0, p1);
}
}
/* ----- Display the sample frequency -------------------------------------- */
static void si_text(int x, int y, double v, const char *unit)
{
const char *pfx;
if (v >= 1e6) {
v /= 1e6;
pfx = "M";
} else if (v >= 1e3) {
v /= 1e3;
pfx = "k";
} else if (v >= 1) {
pfx = " ";
} else if (v >= 1e-3) {
v *= 1e3;
pfx = "m";
} else if (v >= 1e-6) {
v *= 1e6;
pfx = "u";
} else {
v *= 1e9;
pfx = "n";
}
if (v >= 10)
textf(x, y, TEXT_RGBA, "%3d%s%s", (int) (v+0.5), pfx, unit);
else
textf(x, y, TEXT_RGBA, "%3.1f%s%s", v, pfx, unit);
}
static void show_freq(double freq)
{
si_text(FREQ_X, FREQ_Y, freq, "Sa/s");
si_text(INTERVAL_X, INTERVAL_Y, 1/freq, "s/Sa");
}
/* ----- Main event loop --------------------------------------------------- */
static int pos_step(int zoom)
{
return 1 << (MAX_ZOOM-zoom+1);
}
void gui(const uint8_t *buf, int skip, int nibbles, double freq)
{
SDL_Event event;
int pos = (skip+nibbles) >> 1;
int zoom; /* < 0: zoom out; 0: 1 pixel = 1 sample; > 1: zoom in */
int min_zoom = 0;
int i;
while (XRES-CH_XOFF < (nibbles-skip) >> -min_zoom)
min_zoom--;
zoom = min_zoom;
while (1) {
clear();
for (i = 0; i != 4; i++)
textf(0, ch_y(i, 1), LABEL_RGBA, "CH%d", i);
show_buffer(buf, skip, nibbles, CH_XOFF, XRES, zoom, pos);
show_freq(freq);
update();
while (1) {
SDL_WaitEvent(&event);
switch (event.type) {
case SDL_KEYDOWN:
switch (event.key.keysym.sym) {
case SDLK_UP:
if (zoom < MAX_ZOOM)
zoom++;
break;
case SDLK_DOWN:
if (zoom > min_zoom)
zoom--;
break;
case SDLK_LEFT:
pos -= pos_step(zoom);
if (pos < 0)
pos = 0;
break;
case SDLK_RIGHT:
pos += pos_step(zoom);
if (pos > nibbles-1)
pos = nibbles-1;
break;
case SDLK_RETURN:
case SDLK_q:
return;
default:
printf("%x\n", event.key.keysym.sym);
continue;
}
break;
case SDL_QUIT:
return;
default:
continue;
}
break;
}
}
}