altos/draw: Rename test app to 'lco-test'
[fw/altos] / src / draw / lco-test.c
1 /*
2  * Copyright © 2023 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #define IMAGE_SCALE     8
20 #define WIDTH   128
21 #define HEIGHT  64
22
23 #define IMAGE_WIDTH     (WIDTH * IMAGE_SCALE)
24 #define IMAGE_HEIGHT    (HEIGHT * IMAGE_SCALE)
25
26 #define DEFAULT_WIDTH   IMAGE_WIDTH
27 #define DEFAULT_HEIGHT  IMAGE_HEIGHT
28
29 #define TIMEOUT 50
30
31 #define PASS_KEYS
32 #include "frame.c"
33 #include "ao_draw.h"
34
35 #define STRIDE  ((WIDTH + 31) / 32)
36
37 static uint32_t bits[STRIDE * HEIGHT];
38
39 static struct ao_bitmap fb = {
40         .base = bits,
41         .stride = STRIDE,
42         .width = WIDTH,
43         .height = HEIGHT
44 };
45
46 #define BIG_FONT FrutigerLT_Roman_64_font
47 #define VOLT_FONT FrutigerLT_Roman_64_font
48 #define SMALL_FONT NotoMono_12_font
49 #define TINY_FONT NotoMono_10_font
50 #define LOGO_FONT BenguiatGothicStd_Bold_26_font
51
52 #define LABEL_Y         (int16_t) (SMALL_FONT.ascent)
53 #define VALUE_Y         (int16_t) (LABEL_Y + BIG_FONT.ascent)
54 #define BOX_X           2
55 #define PAD_X           90
56 #define BOX_LABEL_X     30
57 #define VOLT_LABEL_X    25
58 #define RSSI_LABEL_X    15
59 #define PAD_LABEL_X     95
60 #define SEP_X           (PAD_X - 10)
61
62 static int      box_number = 1;
63 static int      pad_number = 1;
64 static int      do_polys = 1;
65 static int      scan_number = 0;
66
67 static const struct ao_coord trek[] = {
68         { .x = 90, .y = 0 },
69         { .x = 60, .y = 40 },
70         { .x = 90, .y = 20 },
71         { .x = 120, .y = 40 },
72 };
73
74 #define NCOORD_TREK (sizeof(trek)/sizeof(trek[0]))
75
76 static const struct ao_coord donut[] = {
77         { .x = 30, .y = 0 },
78         { .x = 0, .y = 30 },
79         { .x = 30, .y = 60 },
80         { .x = 60, .y = 30 },
81         { .x = 30, .y = 0 },
82         { .x = 30, .y = 10 },
83         { .x = 50, .y = 30 },
84         { .x = 30, .y = 50 },
85         { .x = 10, .y = 30 },
86         { .x = 30, .y = 10 },
87 };
88
89 #define NCOORD_DONUT (sizeof(donut)/sizeof(donut[0]))
90
91 static const struct ao_coord bowtie[] = {
92         { .x = 0, .y = 0 },
93         { .x = 32, .y = 32 },
94         { .x = 0, .y = 32 },
95         { .x = 32, .y = 0 },
96 };
97
98 #define NCOORD_BOWTIE (sizeof(bowtie)/sizeof(bowtie[0]))
99
100 static const struct ao_transform logo_transform = {
101         .x_scale = 48, .x_off = 0,
102         .y_scale = 48, .y_off = 0,
103 };
104
105 #define SCAN_X          (WIDTH - 100) / 2
106 #define SCAN_Y          51
107 #define SCAN_HEIGHT     4
108 #define FOUND_Y         64
109 #define FOUND_WIDTH     14
110 #define MAX_VALID       (WIDTH / FOUND_WIDTH)
111
112 static const struct ao_transform bowtie_transform = {
113         .x_scale = 1, .x_off = 50,
114         .y_scale = 1, .y_off = 20,
115 };
116
117 static const float pad_volts = 12.3f;
118 static const float lco_volts = 4.1f;
119 static const int rssi = -30;
120
121 #define IMAGE_STRIDE    ((IMAGE_WIDTH + 31) / 32)
122
123 static uint32_t image_bits[IMAGE_STRIDE * IMAGE_HEIGHT];
124
125 static int      boxes[] = { 1, 2, 3, 5, 8, 11, 13, 17, 19, 23, 29, 31, 37, 62, 97 };
126
127 static int      max_box = 97;
128
129 #define ARRAYSIZE(a)    (sizeof(a) / sizeof((a)[0]))
130
131 static bool
132 valid_box(int box)
133 {
134         size_t i;
135         if (box == 0)
136                 return true;
137         for (i = 0; i < ARRAYSIZE(boxes); i++)
138                 if (boxes[i] == box)
139                         return true;
140         return false;
141 }
142
143 static void
144 next_box(void)
145 {
146         for (int n = box_number + 1; n <= max_box; n++)
147                 if (valid_box(n)) {
148                         box_number = n;
149                         return;
150                 }
151         box_number = 0;
152 }
153
154 static void
155 prev_box(void)
156 {
157         for (int n = box_number - 1; n >= 0; n--)
158                 if (valid_box(n)) {
159                         box_number = n;
160                         return;
161                 }
162         box_number = max_box;
163 }
164
165 void HandleExpose(Display *dpy, Window win, GC gc)
166 {
167         char    str[64];
168         int     i;
169         int     v;
170         int16_t b;
171
172         ao_rect(&fb, 0, 0, WIDTH, HEIGHT, 0xffffffff, AO_COPY);
173
174         if (do_polys == 1)
175                 current_timeout = TIMEOUT;
176         else
177                 current_timeout = 0;
178         switch (do_polys) {
179         case 1:
180                 ao_logo(&fb, &logo_transform, &LOGO_FONT, 0x00000000, AO_COPY);
181                 if (scan_number) {
182                         ao_rect(&fb, SCAN_X, SCAN_Y, (int16_t) scan_number, SCAN_HEIGHT, 0x00000000, AO_COPY);
183                         b = 0;
184                         v = 0;
185                         for (i = scan_number; i > 1; i--) {
186                                 if (valid_box(i)) {
187                                         v++;
188                                         if (v == MAX_VALID)
189                                                 break;
190                                 }
191                         }
192                         for (; i <= scan_number; i++) {
193                                 if (valid_box(i)) {
194                                         sprintf(str, "%02d", i);
195                                         ao_text(&fb, &TINY_FONT, 0 + FOUND_WIDTH * b, FOUND_Y, str, 0x00000000, AO_COPY);
196                                         b++;
197                                 }
198                         }
199                 }
200                 break;
201         case 2:
202                 ao_poly(&fb, trek, NCOORD_TREK, NULL, 0x00000000, AO_COPY);
203                 ao_poly(&fb, donut, NCOORD_DONUT, NULL, 0x00000000, AO_COPY);
204                 break;
205         case 3:
206                 ao_poly(&fb, bowtie, NCOORD_BOWTIE, &bowtie_transform, 0x00000000, AO_COPY);
207                 break;
208         default:
209         case 0:
210                 switch (box_number) {
211                 case 0:
212                         sprintf(str, "%4.1f", lco_volts);
213                         ao_text(&fb, &VOLT_FONT, BOX_X, VALUE_Y, str, 0x00000000, AO_COPY);
214                         ao_text(&fb, &SMALL_FONT, VOLT_LABEL_X, LABEL_Y, "LCO Battery", 0x00000000, AO_COPY);
215                         break;
216                 default:
217                         switch (pad_number) {
218                         case -1:
219                                 sprintf(str, "%4.1f", pad_volts);
220                                 ao_text(&fb, &VOLT_FONT, BOX_X, VALUE_Y, str, 0x00000000, AO_COPY);
221                                 ao_text(&fb, &SMALL_FONT, VOLT_LABEL_X, LABEL_Y, "Pad Battery", 0x00000000, AO_COPY);
222                                 break;
223                         case 0:
224                                 sprintf(str, "%4d", rssi);
225                                 ao_text(&fb, &VOLT_FONT, BOX_X, VALUE_Y, str, 0x00000000, AO_COPY);
226                                 ao_text(&fb, &SMALL_FONT, RSSI_LABEL_X, LABEL_Y, "Signal Strength", 0x00000000, AO_COPY);
227                                 break;
228                         default:
229                                 sprintf(str, "%02d", box_number);
230                                 ao_text(&fb, &BIG_FONT, BOX_X, VALUE_Y, str, 0x00000000, AO_COPY);
231                                 ao_text(&fb, &SMALL_FONT, BOX_LABEL_X, LABEL_Y, "Box", 0x00000000, AO_COPY);
232
233                                 sprintf(str, "%d", pad_number);
234                                 ao_text(&fb, &BIG_FONT, PAD_X, VALUE_Y, str, 0x00000000, AO_COPY);
235                                 ao_text(&fb, &SMALL_FONT, PAD_LABEL_X, LABEL_Y, "Pad", 0x00000000, AO_COPY);
236
237                                 ao_rect(&fb, SEP_X, 0, 2, HEIGHT, 0x00000000, AO_COPY);
238                         }
239                         break;
240                 }
241                 break;
242         }
243
244         XImage *source_image = XCreateImage(dpy, visual, 1, XYBitmap, 0, (char *) bits, WIDTH, HEIGHT, 32, STRIDE*4);
245         XImage *image = XCreateImage(dpy, visual, 1, XYBitmap, 0, (char *) image_bits, IMAGE_WIDTH, IMAGE_HEIGHT, 32, IMAGE_STRIDE * 4);
246         int ix, iy;
247         int dx, dy;
248
249         for (iy = 0; iy < HEIGHT; iy++) {
250                 for (ix = 0; ix < WIDTH; ix++) {
251                         unsigned long bit = XGetPixel(source_image, ix, iy);
252                         for (dy = 0; dy < IMAGE_SCALE; dy++) {
253
254                                 for (dx = 0; dx < IMAGE_SCALE; dx++) {
255                                         XPutPixel(image, ix * IMAGE_SCALE + dx, iy * IMAGE_SCALE + dy, bit);
256                                 }
257                         }
258                 }
259         }
260         XSetForeground(dpy, gc, WhitePixel(dpy, screen));
261         XSetBackground(dpy, gc, BlackPixel(dpy, screen));
262         XPutImage(dpy, win, gc, image, 0, 0, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
263         free(image);
264 }
265
266 void
267 HandleKeyPress(Display *dpy, Window win, GC gc, XEvent *ev)
268 {
269         char    string[10];
270         if (XLookupString ((XKeyEvent *) ev, string, sizeof (string), 0, 0) >= 1) {
271                 switch (string[0]) {
272                 case 'q':
273                         exit (0);
274                 case 'p':
275                         if (box_number != 0) {
276                                 pad_number++;
277                                 if (pad_number > 8)
278                                         pad_number = -1;
279                         }
280                         break;
281                 case 'P':
282                         if (box_number != 0) {
283                                 pad_number--;
284                                 if (pad_number < -1)
285                                         pad_number = 8;
286                         }
287                         break;
288                 case 'b':
289                         next_box();
290                         break;
291                 case 'B':
292                         prev_box();
293                         break;
294                 case 'i':
295                         do_polys++;
296                         if (do_polys == 4)
297                                 do_polys = 0;
298                         if (do_polys == 1)
299                                 scan_number = 0;
300                         break;
301                 case 'I':
302                         do_polys--;
303                         if (do_polys < 0)
304                                 do_polys = 4;
305                         if (do_polys == 1)
306                                 scan_number = 0;
307                         break;
308                 case 's':
309                         if (scan_number < 99)
310                                 scan_number++;
311                         break;
312                 case 'S':
313                         if (scan_number > 0)
314                                 scan_number--;
315                         break;
316                 case 'c':
317                         break;
318                 }
319                 HandleExpose(dpy, win, gc);
320         }
321 }
322
323 void
324 HandleKeyRelease(Display *dpy, Window win, GC gc, XEvent *ev)
325 {
326         (void) dpy;
327         (void) win;
328         (void) gc;
329         (void) ev;
330 }
331
332 void
333 HandleTimeout(Display *dpy, Window win, GC gc)
334 {
335         if (do_polys == 1) {
336                 if (scan_number < 99)
337                         scan_number++;
338                 else {
339                         box_number = boxes[0];
340                         pad_number = 1;
341                         do_polys = 0;
342                 }
343                 HandleExpose(dpy, win, gc);
344         }
345 }