Root/ubb-la/gui.c

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
36/* ----- Colors ------------------------------------------------------------ */
37
38
39#define TEXT_RGBA 0xffffffff /* general text */
40#define UNIT_RGBA 0xb0e0ffff /* units */
41#define MAP_BUF_RGBA 0x808080ff /* buffer in the map */
42#define MAP_VIEW_RGBA 0xffffffff /* current view in the map */
43#define CENTER_RGBA 0x5080ffff /* center marker */
44#define USER_REF_RGBA 0x00ff40ff /* user reference marker */
45#define LEVEL_RGBA 0xffff00ff /* constant level or single change */
46#define BOUNCE_RGBA 0xff8080ff /* bouncing signal */
47#define LABEL_RGBA 0xffffffff /* channel label */
48#define DIV_RGBA 0x808080ff /* divisions */
49
50
51/* ----- Layout ------------------------------------------------------------ */
52
53
54#define XCENTER ((XRES+CH_XOFF)/2)
55#define XWIDTH (XRES-CH_XOFF)
56
57#define MAP_BUF_Y0 2
58#define MAP_BUF_Y1 8
59#define MAP_VIEW_Y0 0
60#define MAP_VIEW_Y1 10
61
62#define CENTER_W 8
63#define CENTER_Y0 20
64#define CENTER_Y1 (CENTER_Y0+4)
65
66#define USER_REF_W 8
67#define USER_REF_Y0 92
68#define USER_REF_Y1 (USER_REF_Y0+4)
69
70#define CH_XOFF 30
71#define CH_YOFF 30
72#define CH_SKIP 16
73#define CH_HEIGHT 8
74
75#define DIV_X 32
76#define DIV_Y 6
77
78#define MAX_ZOOM 3
79
80#define UNIT_GAP 2 /* space between number and unit */
81
82#define POS_T_X 182
83#define POS_SAMP_X 270
84#define POS_Y (MEAS_DIV_Y-8-3)
85
86#define MEAS_DIV_Y (FREQ_Y-3-1)
87
88#define FREQ_X 0
89#define FREQ_Y 222
90#define INTERVAL_X 0
91#define INTERVAL_Y (FREQ_Y+8+2)
92#define DIV_SAMP_X (DIV_INT_X-8)
93#define DIV_SAMP_Y FREQ_Y
94#define DIV_INT_X 80
95#define DIV_INT_Y INTERVAL_Y
96
97#define USER_T_X POS_T_X
98#define USER_T_Y FREQ_Y
99#define USER_SAMP_X POS_SAMP_X
100#define USER_SAMP_Y USER_T_Y
101#define USER_FREQ_X POS_T_X
102#define USER_FREQ_Y (USER_T_Y+8+2)
103
104
105/* ----- Miscellaneous definitions ----------------------------------------- */
106
107
108#define REPEAT_DELAY_MS 300
109#define REPEAT_INTERVAL_MS 30
110
111static SDL_Surface *surf;
112static int user_ref = -1;
113
114
115/* ----- Helper functions -------------------------------------------------- */
116
117
118static inline uint8_t get_sample(const uint8_t *buf, unsigned i)
119{
120    return (buf[i >> 1] >> 4*(~i & 1)) & 0xf;
121}
122
123
124/* ----- SDL initialization and screen-wide functions ---------------------- */
125
126
127void gui_init(void)
128{
129    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
130        fprintf(stderr, "SDL_init: %s\n", SDL_GetError());
131        exit(1);
132    }
133    atexit(SDL_Quit);
134
135    surf = SDL_SetVideoMode(XRES, YRES, 0, SDL_SWSURFACE);
136    if (!surf) {
137        fprintf(stderr, "SDL_SetVideoMode: %s\n", SDL_GetError());
138        exit(1);
139    }
140
141    SDL_EnableKeyRepeat(REPEAT_DELAY_MS, REPEAT_INTERVAL_MS);
142
143#ifdef BEN
144    SDL_ShowCursor(SDL_DISABLE);
145#endif
146}
147
148
149static void clear(void)
150{
151    SDL_LockSurface(surf);
152    SDL_FillRect(surf, NULL, SDL_MapRGB(surf->format, 0, 0, 0));
153}
154
155
156static void update(void)
157{
158    SDL_UnlockSurface(surf);
159    SDL_UpdateRect(surf, 0, 0, 0, 0);
160}
161
162
163/* ----- Text output ------------------------------------------------------- */
164
165
166/*
167 * stringColor from SDL_gfx fails for some reason. SDL_ttf is just too much
168 * compatibility trouble. So we do our own.
169 */
170
171static void textf(int x, int y, uint32_t color, const char *fmt, ...)
172{
173    va_list ap;
174    char *tmp, *s;
175    uint8_t *p;
176    int n, ix, iy;
177
178    va_start(ap, fmt);
179    n = vsnprintf(NULL, 0, fmt, ap);
180    va_end(ap);
181    tmp = alloca(n+1);
182    va_start(ap, fmt);
183    vsnprintf(tmp, n+1, fmt, ap);
184    va_end(ap);
185
186    for (s = tmp; *s; s++) {
187        p = gfxPrimitivesFontdata+(*s << 3);
188        for (iy = 0; iy != 8; iy++) {
189            for (ix = 0; ix != 8; ix++)
190                if ((*p << ix) & 0x80)
191                    pixelColor(surf, x+ix, y+iy, color);
192            p++;
193        }
194        x += 8;
195    }
196}
197
198
199/* ----- Map of buffer and view -------------------------------------------- */
200
201
202static void show_map(int skip, int nibbles, int s0, int s1)
203{
204    int w = nibbles-skip;
205    int m = (nibbles-skip) >> 1;
206    int scale = 0;
207    int x0, x1;
208
209    while (w >= XWIDTH/2) {
210        w >>= 1;
211        scale++;
212    }
213    boxColor(surf, XCENTER-(w >> 1), MAP_BUF_Y0,
214        XCENTER+(w >> 1), MAP_BUF_Y1, MAP_BUF_RGBA);
215    x0 = (s0-m+(XCENTER << scale)) >> scale;
216    x1 = x0+((s1-s0) >> scale);
217    rectangleColor(surf, x0, MAP_VIEW_Y0, x1, MAP_VIEW_Y1, MAP_VIEW_RGBA);
218}
219
220
221/* ----- Waveform elements ------------------------------------------------- */
222
223
224static inline int ch_y(int ch, int v)
225{
226    return CH_YOFF+CH_SKIP*ch+(v ? 0 : CH_HEIGHT);
227}
228
229
230static void bounce(int x, int ch)
231{
232    vlineColor(surf, x, ch_y(ch, 0), ch_y(ch, 1), BOUNCE_RGBA);
233}
234
235
236static void change(int x, int ch)
237{
238    vlineColor(surf, x, ch_y(ch, 0), ch_y(ch, 1), LEVEL_RGBA);
239}
240
241
242static void level(int x0, int x1, int ch, int v)
243{
244    if (x0 == x1)
245        pixelColor(surf, x0, ch_y(ch, v), LEVEL_RGBA);
246    else
247        hlineColor(surf, x0, x1, ch_y(ch, v), LEVEL_RGBA);
248}
249
250
251/* ----- Show (part of) a buffer ------------------------------------------- */
252
253
254static void show_buffer_zoom_in(const uint8_t *buf, int skip, int nibbles,
255    int x0, int x1)
256{
257    int f = (x1-x0)/(nibbles-skip);
258    int x, i, j;
259    int last[4] = { -1, -1, -1, -1};
260    uint8_t v, bit;
261
262    DEBUG("in: %d-%d (%d) Sa; %d-%d (%d) pix; %d f (%d)\n",
263        skip, nibbles, nibbles-skip, x0, x1, x1-x0, f, f*(nibbles-skip));
264    x = x0;
265    for (i = skip; i != nibbles; i++) {
266        v = get_sample(buf, i);
267        for (j = 0; j != 4; j++) {
268            bit = (v >> j) & 1;
269            if (bit == last[j] || last[j] == -1) {
270                level(x, x+f-1, j, bit);
271            } else {
272                change(x, j);
273                level(x+1, x+f-1, j, bit);
274            }
275            last[j] = bit;
276        }
277        x += f;
278    }
279}
280
281
282static void show_buffer_zoom_out(const uint8_t *buf, int skip, int nibbles,
283    int x0, int x1)
284{
285    int f = (nibbles-skip)/(x1-x0);
286    int x;
287    int i = skip, n, j;
288    int changes[4], last[4] = { -1, -1, -1, -1}, next[4];
289    uint8_t v, bit;
290
291    DEBUG("out: %d-%d (%d) Sa; %d-%d (%d) pix; %d f (%d)\n",
292        skip, nibbles, nibbles-skip, x0, x1, x1-x0, f, f*(x1-x0));
293    for (x = x0; x != x1; x++) {
294        n = i+f;
295        for (j = 0; j != 4; j++) {
296            next[j] = last[j];
297            changes[j] = 0;
298        }
299        while (i != n) {
300            v = get_sample(buf, i);
301            for (j = 0; j != 4; j++) {
302                bit = (v >> j) & 1;
303                if (bit != next[j]) {
304                    changes[j]++;
305                    next[j] = bit;
306                }
307            }
308            i++;
309        }
310        for (j = 0; j != 4; j++) {
311            if (changes[j] > 1)
312                bounce(x, j);
313            else if (!changes[j] || last[j] == -1)
314                level(x, x, j, next[j]);
315            else
316                change(x, j);
317            last[j] = next[j];
318        }
319    }
320}
321
322
323static void user_ref_marker(int x0, int x1, int x)
324{
325    if (x < 0 || x > x1-x0)
326        return;
327    filledTrigonColor(surf, x0+x, USER_REF_Y0,
328        x0+x-USER_REF_W/2, USER_REF_Y1,
329        x0+x+USER_REF_W/2, USER_REF_Y1, USER_REF_RGBA);
330}
331
332
333static void show_buffer(const uint8_t *buf, int skip, int nibbles,
334    int x0, int x1, int zoom, int pos)
335{
336    int xm, w, s, p0, p1;
337    int d, dp, ref;
338
339    xm = (x0+x1) >> 1;
340    dp = pos-((nibbles-skip) >> 1);
341    ref = user_ref+skip;
342    DEBUG("show: %d-%d Sa; %d-%d pix; pos %d dp %d; xm %d xcenter %d\n",
343        skip, nibbles, x0, x1, pos, dp, xm, XCENTER);
344    if (zoom < 0) {
345        s = (x1-x0) << -zoom;
346        show_map(skip, nibbles, pos-s/2, pos+s/2);
347        w = (nibbles-skip) >> -zoom;
348        p0 = xm-(w >> 1)-(dp >> -zoom);
349        p1 = xm+((w+1) >> 1)-(dp >> -zoom);
350        if (p0 < x0) {
351            skip += (x0-p0) << -zoom;
352            p0 = x0;
353        }
354        if (p1 > x1) {
355            nibbles -= (p1-x1) << -zoom;
356            p1 = x1;
357        }
358        show_buffer_zoom_out(buf, skip, nibbles, p0, p1);
359        if (user_ref != -1)
360            user_ref_marker(p0, p1, (ref-skip) >> -zoom);
361    } else {
362        s = (x1-x0) >> zoom;
363        show_map(skip, nibbles, pos-s/2, pos+s/2);
364        w = (nibbles-skip) << zoom;
365        p0 = xm-(w >> 1)-(dp << zoom);
366        p1 = xm+((w+1) >> 1)-(dp << zoom);
367        if (p0 < x0) {
368            d = ((x0-p0)+(1 << zoom)-1) >> zoom;
369            skip += d;
370            p0 += d << zoom;
371        }
372        if (p1 > x1) {
373            d = ((p1-x1)+(1 << zoom)-1) >> zoom;
374            nibbles -= d;
375            p1 -= d << zoom;
376        }
377        show_buffer_zoom_in(buf, skip, nibbles, p0, p1);
378        if (user_ref != -1)
379            user_ref_marker(p0, p1, (ref-skip) << zoom);
380    }
381}
382
383
384/* ----- Display various settings ------------------------------------------ */
385
386
387static void si_text(int x, int y, double v, const char *unit, int digits)
388{
389    const char *pfx;
390
391    if (v >= 1e6) {
392        v /= 1e6;
393        pfx = "M";
394    } else if (v >= 1e3) {
395        v /= 1e3;
396        pfx = "k";
397    } else if (v >= 1) {
398        pfx = " ";
399    } else if (v >= 1e-3) {
400        v *= 1e3;
401        pfx = "m";
402    } else if (v >= 1e-6) {
403        v *= 1e6;
404        pfx = "u";
405    } else {
406        v *= 1e9;
407        pfx = "n";
408    }
409    if (v >= 10 && digits == 3)
410        textf(x, y, TEXT_RGBA, "%3d", (int) (v+0.5));
411    else if (v >= 100 && digits == 4)
412        textf(x, y, TEXT_RGBA, "%4d", (int) (v+0.5));
413    else if (digits == 7)
414        textf(x, y, TEXT_RGBA, "%*.*f", digits, 3, v);
415    else if (v >= 100)
416        textf(x, y, TEXT_RGBA, "%*.*f", digits, digits-4, v);
417    else if (v >= 10)
418        textf(x, y, TEXT_RGBA, "%*.*f", digits, digits-3, v);
419    else
420        textf(x, y, TEXT_RGBA, "%*.*f", digits, digits-2, v);
421    textf(x+digits*8+UNIT_GAP, y, UNIT_RGBA, "%s%s", pfx, unit);
422}
423
424
425static void show_freq(double freq, int zoom)
426{
427    int div;
428
429    si_text(FREQ_X, FREQ_Y, freq, "Sa/s", 3);
430    si_text(INTERVAL_X, INTERVAL_Y, 1/freq, "s/Sa", 3);
431    div = (DIV_X >> MAX_ZOOM) << (MAX_ZOOM-zoom);
432    textf(DIV_SAMP_X, DIV_SAMP_Y, TEXT_RGBA, "%4d", div);
433    textf(DIV_SAMP_X+4*8+UNIT_GAP, DIV_SAMP_Y, UNIT_RGBA, "Sa/div", div);
434    si_text(DIV_INT_X, DIV_INT_Y, div/freq, "s/div", 3);
435}
436
437
438static void show_position(double freq, int pos)
439{
440    si_text(POS_T_X, POS_Y, pos/freq, "s", 7);
441    hlineColor(surf, 0, XRES-1, MEAS_DIV_Y, DIV_RGBA);
442    si_text(POS_T_X, POS_Y, pos/freq, "s", 7);
443    hlineColor(surf, 0, XRES-1, MEAS_DIV_Y, DIV_RGBA);
444
445    textf(POS_SAMP_X, POS_Y, TEXT_RGBA, "%4d", pos);
446    textf(POS_SAMP_X+4*8+UNIT_GAP, POS_Y, UNIT_RGBA, "Sa", div);
447}
448
449
450static void show_user_ref(double freq, int pos, int user_ref)
451{
452    si_text(USER_T_X, USER_T_Y, fabs(user_ref-pos)/freq, "s", 7);
453
454    textf(USER_SAMP_X,USER_SAMP_Y, TEXT_RGBA, "%4d",
455        pos > user_ref ? pos-user_ref : user_ref-pos);
456    textf(USER_SAMP_X+4*8+UNIT_GAP, USER_SAMP_Y, UNIT_RGBA, "Sa", div);
457
458    if (pos != user_ref)
459        si_text(USER_FREQ_X, USER_FREQ_Y,
460            freq/fabs(user_ref-pos), "Hz", 7);
461}
462
463
464/* ----- Show divisions ---------------------------------------------------- */
465
466
467static void show_divisions(void)
468{
469    int n = XWIDTH/2/DIV_X;
470    int i;
471
472    for (i = -n; i <= n; i++)
473        vlineColor(surf, XCENTER+i*DIV_X,
474            ch_y(0, 1)-DIV_Y, ch_y(3, 0)+DIV_Y,
475            i ? DIV_RGBA : CENTER_RGBA);
476}
477
478
479/* ----- Main event loop --------------------------------------------------- */
480
481
482static int pos_step(int zoom)
483{
484    return zoom == MAX_ZOOM ? 1 : 1 << (MAX_ZOOM-zoom+1);
485}
486
487
488static int smart_pos(const uint8_t *buf, int skip, int nibbles,
489    int pos, int dir)
490{
491    uint8_t ref;
492
493    if (dir < 0) {
494        if (!pos)
495            return pos;
496        ref = get_sample(buf, skip+pos-1);
497    } else {
498        ref = get_sample(buf, skip+pos);
499    }
500    do {
501        pos += dir;
502        if (pos < 0 || pos == nibbles-skip)
503            return pos-dir;
504    } while (get_sample(buf, skip+pos) == ref);
505    if (dir < 0)
506        pos++;
507    return pos;
508}
509
510
511void gui(const uint8_t *buf, int skip, int nibbles, double freq)
512{
513    SDL_Event event;
514    int pos = (skip+nibbles) >> 1;
515    int zoom; /* < 0: zoom out; 0: 1 pixel = 1 sample; > 1: zoom in */
516    int min_zoom = 0;
517    int i, shift;
518
519    while (XWIDTH < (nibbles-skip) >> -min_zoom)
520        min_zoom--;
521    zoom = min_zoom;
522    while (1) {
523        clear();
524        for (i = 0; i != 4; i++)
525            textf(0, ch_y(i, 1), LABEL_RGBA, "CH%d", i);
526        show_divisions();
527        filledTrigonColor(surf, XCENTER, CENTER_Y1,
528            XCENTER-CENTER_W/2, CENTER_Y0,
529            XCENTER+CENTER_W/2, CENTER_Y0, CENTER_RGBA);
530        show_buffer(buf, skip, nibbles, CH_XOFF, XRES, zoom, pos);
531        show_freq(freq, zoom);
532        show_position(freq, pos);
533        if (user_ref != -1)
534            show_user_ref(freq, pos, user_ref);
535        update();
536
537        while (1) {
538            SDL_WaitEvent(&event);
539            switch (event.type) {
540            case SDL_KEYDOWN:
541                shift = event.key.keysym.mod & KMOD_SHIFT;
542                switch (event.key.keysym.sym) {
543                case SDLK_UP:
544                    if (zoom < MAX_ZOOM)
545                        zoom++;
546                    break;
547                case SDLK_DOWN:
548                    if (zoom > min_zoom)
549                        zoom--;
550                    break;
551                case SDLK_LEFT:
552                    if (shift) {
553                        pos = smart_pos(buf,
554                            skip, nibbles, pos, -1);
555                    } else {
556                        pos -= pos_step(zoom);
557                        if (pos < 0)
558                            pos = 0;
559                    }
560                    break;
561                case SDLK_RIGHT:
562                    if (shift) {
563                        pos = smart_pos(buf,
564                            skip, nibbles, pos, 1);
565                    } else {
566                        pos += pos_step(zoom);
567                        if (pos > nibbles-skip-1)
568                            pos = nibbles-skip-1;
569                    }
570                    break;
571                case SDLK_SPACE:
572                    if (pos == user_ref)
573                        user_ref = -1;
574                    else
575                        user_ref = pos;
576                    break;
577                case SDLK_RETURN:
578                case SDLK_q:
579                    return;
580                case SDLK_LSHIFT:
581                case SDLK_RSHIFT:
582                    continue;
583                default:
584                    printf("%x\n", event.key.keysym.sym);
585                    continue;
586                }
587                break;
588            case SDL_QUIT:
589                return;
590            default:
591                continue;
592            }
593            break;
594        }
595    }
596}
597

Archive Download this file

Branches:
master



interactive