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