Root/ubb-la/gui.c

Source at commit 06c5f6b0fa8cc493d93cfb87f8cbc62a877c9bc7 created 6 years 8 months ago.
By Werner Almesberger, ubb-la/gui.c (textf): use vsnprintf+valloca instead of vasprintf
1/*
2 * gui.c - Graphical output for ubb-la
3 *
4 * Written 2013 by Werner Almesberger
5 * Copyright 2013 Werner Almesberger
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13#include <stdarg.h>
14#include <stdint.h>
15#include <stdlib.h>
16#include <alloca.h>
17
18#include "SDL.h"
19#include "SDL_gfxPrimitives.h"
20#include "SDL_gfxPrimitives_font.h"
21
22#include "gui.h"
23
24
25#if 0
26#define DEBUG(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
27#else
28#define DEBUG(fmt, ...)
29#endif
30
31
32#define XRES 320 /* canvas width */
33#define YRES 240 /* canvas height */
34
35#define TEXT_RGBA 0xffffffff /* general text */
36#define MAP_BUF_RGBA 0x808080ff /* buffer in the map */
37#define MAP_VIEW_RGBA 0xffffffff /* current view in the map */
38#define LEVEL_RGBA 0xffff00ff /* constant level or single change */
39#define BOUNCE_RGBA 0xff8080ff /* bouncing signal */
40#define LABEL_RGBA 0xffffffff /* channel label */
41#define DIV_RGBA 0x808080ff /* divisions */
42
43#define XCENTER ((XRES+CH_XOFF)/2)
44#define XWIDTH (XRES-CH_XOFF)
45
46#define MAP_BUF_Y0 2
47#define MAP_BUF_Y1 8
48#define MAP_VIEW_Y0 0
49#define MAP_VIEW_Y1 10
50
51#define CH_XOFF 30
52#define CH_YOFF 30
53#define CH_SKIP 16
54#define CH_HEIGHT 8
55
56#define DIV_X 32
57#define DIV_Y 6
58
59#define MAX_ZOOM 3
60
61#define FREQ_X 0
62#define FREQ_Y 220
63#define INTERVAL_X 0
64#define INTERVAL_Y 230
65#define DIV_SAMP_X (80-8)
66#define DIV_SAMP_Y FREQ_Y
67#define DIV_INT_X 80
68#define DIV_INT_Y INTERVAL_Y
69
70#define REPEAT_DELAY_MS 300
71#define REPEAT_INTERVAL_MS 30
72
73static SDL_Surface *surf;
74
75
76/* ----- SDL initialization and screen-wide functions ---------------------- */
77
78
79void gui_init(void)
80{
81    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
82        fprintf(stderr, "SDL_init: %s\n", SDL_GetError());
83        exit(1);
84    }
85    atexit(SDL_Quit);
86
87    surf = SDL_SetVideoMode(XRES, YRES, 0, SDL_SWSURFACE);
88    if (!surf) {
89        fprintf(stderr, "SDL_SetVideoMode: %s\n", SDL_GetError());
90        exit(1);
91    }
92
93    SDL_EnableKeyRepeat(REPEAT_DELAY_MS, REPEAT_INTERVAL_MS);
94}
95
96
97static void clear(void)
98{
99    SDL_LockSurface(surf);
100    SDL_FillRect(surf, NULL, SDL_MapRGB(surf->format, 0, 0, 0));
101}
102
103
104static void update(void)
105{
106    SDL_UnlockSurface(surf);
107    SDL_UpdateRect(surf, 0, 0, 0, 0);
108}
109
110
111/* ----- Text output ------------------------------------------------------- */
112
113
114/*
115 * stringColor from SDL_gfx fails for some reason. SDL_ttf is just too much
116 * compatibility trouble. So we do our own.
117 */
118
119static void textf(int x, int y, uint32_t color, const char *fmt, ...)
120{
121    va_list ap;
122    char *tmp, *s;
123    uint8_t *p;
124    int n, ix, iy;
125
126    va_start(ap, fmt);
127    n = vsnprintf(NULL, 0, fmt, ap);
128    va_end(ap);
129    tmp = alloca(n+1);
130    va_start(ap, fmt);
131    vsnprintf(tmp, n+1, fmt, ap);
132    va_end(ap);
133
134    for (s = tmp; *s; s++) {
135        p = gfxPrimitivesFontdata+(*s << 3);
136        for (iy = 0; iy != 8; iy++) {
137            for (ix = 0; ix != 8; ix++)
138                if ((*p << ix) & 0x80)
139                    pixelColor(surf, x+ix, y+iy, color);
140            p++;
141        }
142        x += 8;
143    }
144}
145
146
147/* ----- Map of buffer and view -------------------------------------------- */
148
149
150static void show_map(int skip, int nibbles, int s0, int s1)
151{
152    int w = nibbles-skip;
153    int m = (nibbles+skip) >> 1;
154    int scale = 0;
155
156    while (w >= XWIDTH/2) {
157        w >>= 1;
158        scale++;
159    }
160    boxColor(surf, XCENTER-(w >> 1), MAP_BUF_Y0,
161        XCENTER+(w >> 1), MAP_BUF_Y1, MAP_BUF_RGBA);
162    rectangleColor(surf, (s0-m+(XCENTER << scale)) >> scale, MAP_VIEW_Y0,
163        (s1-m+(XCENTER << scale)) >> scale, MAP_VIEW_Y1, MAP_VIEW_RGBA);
164}
165
166
167/* ----- Waveform elements ------------------------------------------------- */
168
169
170static inline int ch_y(int ch, int v)
171{
172    return CH_YOFF+CH_SKIP*ch+(v ? 0 : CH_HEIGHT);
173}
174
175
176static void bounce(int x, int ch)
177{
178    vlineColor(surf, x, ch_y(ch, 0), ch_y(ch, 1), BOUNCE_RGBA);
179}
180
181
182static void change(int x, int ch)
183{
184    vlineColor(surf, x, ch_y(ch, 0), ch_y(ch, 1), LEVEL_RGBA);
185}
186
187
188static void level(int x0, int x1, int ch, int v)
189{
190    if (x0 == x1)
191        pixelColor(surf, x0, ch_y(ch, v), LEVEL_RGBA);
192    else
193        hlineColor(surf, x0, x1, ch_y(ch, v), LEVEL_RGBA);
194}
195
196
197/* ----- Show (part of) a buffer ------------------------------------------- */
198
199
200static void show_buffer_zoom_in(const uint8_t *buf, int skip, int nibbles,
201    int x0, int x1)
202{
203    int f = (x1-x0)/(nibbles-skip);
204    int x, i, j;
205    int last[4] = { -1, -1, -1, -1};
206    uint8_t v, bit;
207
208    DEBUG("in: %d-%d (%d) Sa %d-%d (%d) pix %d f (%d)\n",
209        skip, nibbles, nibbles-skip, x0, x1, x1-x0, f, f*(nibbles-skip));
210    x = x0;
211    for (i = skip; i != nibbles; i++) {
212        v = (buf[i >> 1] >> 4*(~i & 1)) & 0xf;
213        for (j = 0; j != 4; j++) {
214            bit = (v >> j) & 1;
215            if (bit == last[j] || last[j] == -1) {
216                level(x, x+f-1, j, bit);
217            } else {
218                change(x, j);
219                level(x+1, x+f-1, j, bit);
220            }
221            last[j] = bit;
222        }
223        x += f;
224    }
225}
226
227
228static void show_buffer_zoom_out(const uint8_t *buf, int skip, int nibbles,
229    int x0, int x1)
230{
231    int f = (nibbles-skip)/(x1-x0);
232    int x;
233    int i = skip, n, j;
234    int changes[4], last[4] = { -1, -1, -1, -1}, next[4];
235    uint8_t v, bit;
236
237    DEBUG("out: %d-%d (%d) Sa %d-%d (%d) pix %d f (%d)\n",
238        skip, nibbles, nibbles-skip, x0, x1, x1-x0, f, f*(x1-x0));
239    for (x = x0; x != x1; x++) {
240        n = i+f;
241        for (j = 0; j != 4; j++) {
242            next[j] = last[j];
243            changes[j] = 0;
244        }
245        while (i != n) {
246            v = (buf[i >> 1] >> 4*(~i & 1)) & 0xf;
247            for (j = 0; j != 4; j++) {
248                bit = (v >> j) & 1;
249                if (bit != next[j]) {
250                    changes[j]++;
251                    next[j] = bit;
252                }
253            }
254            i++;
255        }
256        for (j = 0; j != 4; j++) {
257            if (changes[j] > 1)
258                bounce(x, j);
259            else if (!changes[j] || last[j] == -1)
260                level(x, x, j, next[j]);
261            else
262                change(x, j);
263            last[j] = next[j];
264        }
265    }
266}
267
268
269static void show_buffer(const uint8_t *buf, int skip, int nibbles,
270    int x0, int x1, int zoom, int pos)
271{
272    int xm, w, s, p0, p1;
273    int d, dp;
274
275    xm = (x0+x1) >> 1;
276    dp = pos-((nibbles+skip) >> 1);
277    if (zoom < 0) {
278        s = (x1-x0) << -zoom;
279        show_map(skip, nibbles, pos-s/2, pos+s/2);
280        w = (nibbles-skip) >> -zoom;
281        p0 = xm-(w >> 1)-(dp >> -zoom);
282        p1 = xm+((w+1) >> 1)-(dp >> -zoom);
283        if (p0 < x0) {
284            skip += (x0-p0) << -zoom;
285            p0 = x0;
286        }
287        if (p1 > x1) {
288            nibbles -= (p1-x1) << -zoom;
289            p1 = x1;
290        }
291        show_buffer_zoom_out(buf, skip, nibbles, p0, p1);
292    } else {
293        s = (x1-x0) >> zoom;
294        show_map(skip, nibbles, pos-s/2, pos+s/2);
295        w = (nibbles-skip) << zoom;
296        p0 = xm-(w >> 1)-(dp << zoom);
297        p1 = xm+((w+1) >> 1)-(dp << zoom);
298        if (p0 < x0) {
299            d = ((x0-p0)+(1 << zoom)-1) >> zoom;
300            skip += d;
301            p0 += d << zoom;
302        }
303        if (p1 > x1) {
304            d = ((p1-x1)+(1 << zoom)-1) >> zoom;
305            nibbles -= d;
306            p1 -= d << zoom;
307        }
308        show_buffer_zoom_in(buf, skip, nibbles, p0, p1);
309    }
310}
311
312
313/* ----- Display the sample frequency -------------------------------------- */
314
315
316static void si_text(int x, int y, double v, const char *unit)
317{
318    const char *pfx;
319
320    if (v >= 1e6) {
321        v /= 1e6;
322        pfx = "M";
323    } else if (v >= 1e3) {
324        v /= 1e3;
325        pfx = "k";
326    } else if (v >= 1) {
327        pfx = " ";
328    } else if (v >= 1e-3) {
329        v *= 1e3;
330        pfx = "m";
331    } else if (v >= 1e-6) {
332        v *= 1e6;
333        pfx = "u";
334    } else {
335        v *= 1e9;
336        pfx = "n";
337    }
338    if (v >= 10)
339        textf(x, y, TEXT_RGBA, "%3d%s%s", (int) (v+0.5), pfx, unit);
340    else
341        textf(x, y, TEXT_RGBA, "%3.1f%s%s", v, pfx, unit);
342}
343
344
345static void show_freq(double freq, int zoom)
346{
347    int div;
348
349    si_text(FREQ_X, FREQ_Y, freq, "Sa/s");
350    si_text(INTERVAL_X, INTERVAL_Y, 1/freq, "s/Sa");
351    div = (DIV_X >> MAX_ZOOM) << (MAX_ZOOM-zoom);
352    textf(DIV_SAMP_X, DIV_SAMP_Y, TEXT_RGBA, "%4dSa/div", div);
353    si_text(DIV_INT_X, DIV_INT_Y, div/freq, "s/div");
354}
355
356
357/* ----- Show a divisions -------------------------------------------------- */
358
359
360static void show_divisions(void)
361{
362    int n = XWIDTH/2/DIV_X;
363    int i;
364
365    for (i = -n; i <= n; i++)
366        vlineColor(surf, XCENTER+i*DIV_X,
367            ch_y(0, 1)-DIV_Y, ch_y(3, 0)+DIV_Y, DIV_RGBA);
368}
369
370
371/* ----- Main event loop --------------------------------------------------- */
372
373
374static int pos_step(int zoom)
375{
376    return 1 << (MAX_ZOOM-zoom+1);
377}
378
379
380void gui(const uint8_t *buf, int skip, int nibbles, double freq)
381{
382    SDL_Event event;
383    int pos = (skip+nibbles) >> 1;
384    int zoom; /* < 0: zoom out; 0: 1 pixel = 1 sample; > 1: zoom in */
385    int min_zoom = 0;
386    int i;
387
388    while (XWIDTH < (nibbles-skip) >> -min_zoom)
389        min_zoom--;
390    zoom = min_zoom;
391    while (1) {
392        clear();
393        for (i = 0; i != 4; i++)
394            textf(0, ch_y(i, 1), LABEL_RGBA, "CH%d", i);
395        show_divisions();
396        show_buffer(buf, skip, nibbles, CH_XOFF, XRES, zoom, pos);
397        show_freq(freq, zoom);
398        update();
399
400        while (1) {
401            SDL_WaitEvent(&event);
402            switch (event.type) {
403            case SDL_KEYDOWN:
404                switch (event.key.keysym.sym) {
405                case SDLK_UP:
406                    if (zoom < MAX_ZOOM)
407                        zoom++;
408                    break;
409                case SDLK_DOWN:
410                    if (zoom > min_zoom)
411                        zoom--;
412                    break;
413                case SDLK_LEFT:
414                    pos -= pos_step(zoom);
415                    if (pos < 0)
416                        pos = 0;
417                    break;
418                case SDLK_RIGHT:
419                    pos += pos_step(zoom);
420                    if (pos > nibbles-1)
421                        pos = nibbles-1;
422                    break;
423                case SDLK_RETURN:
424                case SDLK_q:
425                    return;
426                default:
427                    printf("%x\n", event.key.keysym.sym);
428                    continue;
429                }
430                break;
431            case SDL_QUIT:
432                return;
433            default:
434                continue;
435            }
436            break;
437        }
438    }
439}
440

Archive Download this file

Branches:
master



interactive