rcbench
Servo and ESC test bench: panel, IOmcu and shared C
Loading...
Searching...
No Matches
esc_stick_int.h
Go to the documentation of this file.
1
17#pragma once
18
19#include <stdbool.h>
20#include <stddef.h>
21#include <stdint.h>
22#include <string.h>
23
24#include "gfx.h"
25#include "prog_registry.h"
26#include "touch_types.h"
27#include "ui_layout.h"
28#include "ui_screen.h"
29#include "ui_text.h"
30#include "ui_theme.h"
31#include "ui_widgets.h"
32
34#define W UI_PANEL_W
36#define H UI_BODY_H
37
46#define SP_FONT (&gfx_font_10x20)
47#define SP_CELL 10
48#define SP_FONT_CRUMB (&gfx_font_12x24)
49#define SP_CRUMB_CELL 12
50#define SP_FONT_HEAD (&gfx_font_20x36)
51#define SP_FONT_NUM (&gfx_font_num_32x44)
52#define LINE_P 20
53
65#define PAD UI_MARGIN
66#define SP_FOOT (H - UI_ALERT_H - 4)
67#define CRUMB_Y (PROG_FULL_Y + 2)
68#define CRUMB_H UI_TAP_MIN
69#define BACK_W 120
70#define BTN_H UI_BTN_H_TALL
72#define BTN_Y (SP_FOOT - BTN_H)
73#define FOOT_BTN_W 168
74#define CARD_Y (CRUMB_Y + CRUMB_H + 6)
75#define CARD_B (BTN_Y - 8)
76
89#define PARM_W 660
90#define ROW_Y0 (CARD_Y + 8)
91#define ROW_H UI_TAP_MIN
92#define ROW_P 56
93#define ROWS_MAX 6
94#define ROW_X (PAD + 8)
95#define ROW_TY ((ROW_H - 20) / 2)
96#define NAME_X (PAD + 46)
97#define STEP_W UI_TAP_MIN
98#define STEP_UP_X (PAD + PARM_W - 8 - STEP_W)
99#define STEP_DN_X (STEP_UP_X - 8 - STEP_W)
100#define CTRL_W 250
101#define CTRL_X (STEP_DN_X - 8 - CTRL_W)
102#define SIDE_X (PAD + PARM_W + 8)
103#define SIDE_W (W - PAD - SIDE_X)
104#define SIDE_IN (SIDE_X + 12)
105#define SIDE_CELLS ((SIDE_W - 24) / SP_CELL)
106#define PAGE_Y (CARD_Y + 8)
107#define PAGE_KEY_W 48
108#define HAND_Y (PAGE_Y + UI_TAP_MIN + 8)
109#define SIDE_B (CARD_B - 6)
110
116#define DEV_Y CARD_Y
117#define DEV_H 48
118#define RUN_Y (DEV_Y + DEV_H + 4)
119#define HELP_P 22
120#define HELP_Y (BTN_Y - 4 - 20 - HELP_P)
121#define RUN_H (HELP_Y - 6 - RUN_Y)
122#define RUN_L0 (RUN_Y + 84)
123#define RUN_LINES 8
124
125/* The panels over the pages -- the RUN warning, the manual steps and a
126 * run's prompt -- from PROG_FULL_Y to the body's margin, and the buttons at
127 * their foot, UI_TAP_MIN tall, which end 4px above the foot row's place
128 * (body y 384 to 431): a second tap on RUN's place lands on no button of
129 * the panel it opened, and the alert band covers none of them. */
130#define SP_PANEL_Y PROG_FULL_Y
131#define SP_PANEL_H (H - PAD - SP_PANEL_Y)
132#define SP_PBTN_H UI_TAP_MIN
134#define SP_PBTN_Y (BTN_Y - 4 - SP_PBTN_H)
135
136_Static_assert(ROW_Y0 + (ROWS_MAX - 1) * ROW_P + ROW_H <= CARD_B - 4,
137 "ROWS_MAX rows fit the parameter card");
138_Static_assert(RUN_L0 + (RUN_LINES - 1) * LINE_P + 20 <= RUN_Y + RUN_H - 2,
139 "a run's lines fit its card");
140_Static_assert(HELP_Y + HELP_P + 20 <= BTN_Y, "the help lines end above the "
141 "foot row");
142_Static_assert(BTN_Y + BTN_H + 4 == UI_ALERT_Y - UI_BAND_H,
143 "the foot row ends 4px above the alert band");
144
145/*
146 * Each page's drawer is its own function, so the main task's stack holds
147 * the buffers of the page on screen and no other. render() runs on the
148 * panel's main task, which tools/stack_check.py holds to its stack. GCC
149 * inlines a static function called once, and inlined, every page's buffers
150 * share render()'s frame: 1760 bytes at -O2. Kept apart, render()'s frame
151 * is 32 bytes and the largest page's 464.
152 */
153#define DRAWER __attribute__((noinline))
154
155/* Where the session is: closed (PROGRAM's list shows), on the profile
156 * list (the makers, or one maker's models), or on a profile's page. */
157typedef enum { STAGE_CLOSED = 0, STAGE_LIST, STAGE_PAGE } prog_stage_t;
158
159/*
160 * A control on this screen is a tap: its press arms it and its release
161 * activates it. The release counts when the same contact makes it inside
162 * the control, no further than TAP_SLOP from the press in x and in y, with
163 * the screen still showing what it showed at the press, and is not an UP
164 * made for the finger (TOUCH_FLAG_NO_TAP). A finger that crosses a control
165 * presses nothing.
166 *
167 * The controls are found by one walk, press_at(), run twice. On the DOWN
168 * it runs as PASS_PRESS: hit() notes the first control under the point and
169 * answers false, so nothing acts. On a release that counts it runs again
170 * as PASS_TAP at the point of the press: hit() answers true and the control
171 * acts, or does not where it has nothing to do, as a page key at the end of
172 * its list.
173 *
174 * Three controls act on the press and are asked with hit_down(): HOLD TO
175 * RUN, whose hold is the gesture; ABORT of a run, on the run's page and on
176 * a manual step, which stops a motor and does so at once; and the search's
177 * keyboard, whose keys follow their own press and release.
178 */
179#define TAP_SLOP 8
180
181/* A helper of this header: static, and not warned of where unused. */
182#define SP_SHARED static __attribute__((unused))
183enum { PASS_PRESS = 0, PASS_TAP };
184
185/* The state both files read and write. The panel moves between the
186 * stages and bumps the revision a render follows; it draws and takes the
187 * session's controls in their places: BACK in the crumb row, READ and
188 * WRITE at the foot (DEFAULTS and RUN, CLOSE and OK), the page keys, and
189 * the rows with their steppers. */
190typedef struct {
191 prog_stage_t stage;
192
193 gfx_rect_t back, read_btn, write_btn;
194 gfx_rect_t page_up, page_dn;
195 gfx_rect_t row[ROWS_MAX], down[ROWS_MAX], up[ROWS_MAX];
196
197 /*
198 * The press that may become a tap: the contact, where it came down,
199 * the control under it, and what the screen showed then. pass says
200 * which of the two walks over the controls is running (hit()).
201 */
202 struct {
203 bool live;
204 uint8_t id;
205 int16_t x, y;
206 gfx_rect_t rect;
207 uint32_t showing;
208 } tap;
209 int pass;
210 bool found;
211 gfx_rect_t found_rect;
212
213 uint32_t rev;
215
216/* In esc_stick_pages.c. */
217extern stick_shared_t pg;
218
219SP_SHARED bool hit(gfx_rect_t r, int px, int py)
220{
221 if (!gfx_rect_contains(r, px, py)) {
222 return false;
223 }
224 if (pg.pass == PASS_TAP) {
225 return true;
226 }
227 if (!pg.found) {
228 pg.found = true;
229 pg.found_rect = r;
230 }
231 return false;
232}
233
239SP_SHARED void draw_crumb(gfx_canvas_t *c, const char *trail)
240{
241 ui_button_font(c, pg.back, TR(LOG_BACK), ui_theme_color(UI_C_PANEL_HI),
242 false, true, SP_FONT);
243 gfx_text(c, pg.back.x + pg.back.w + 16, CRUMB_Y + (CRUMB_H - 24) / 2,
244 trail, SP_FONT_CRUMB, ui_theme_color(UI_C_TEXT_DIM), 1);
245}
246
254SP_SHARED void widget_enum(gfx_canvas_t *c, int y, const char *text,
255 gfx_color_t ink)
256{
257 gfx_text_in(c, (gfx_rect_t){ CTRL_X, (int16_t)(y + ROW_TY), CTRL_W, 20 },
258 text, SP_FONT, ink, 1, GFX_ALIGN_RIGHT);
259}
260
261/* --- text in cells ------------------------------------------------------- */
262
263/*
264 * @p buf cut to @p max_chars cells, with ".." in place of what did not fit.
265 * Cut between characters, never inside one: a cell is a code point and a
266 * German letter is two bytes.
267 */
268SP_SHARED void sp_cut(char *buf, size_t n, int max_chars)
269{
270 if (max_chars < 3 || gfx_text_cells(buf) <= max_chars) {
271 return;
272 }
273 const size_t at = gfx_text_prefix(buf, max_chars - 2);
274 if (at + 3u <= n) {
275 buf[at] = '.';
276 buf[at + 1u] = '.';
277 buf[at + 2u] = '\0';
278 }
279}
280
281/*
282 * @p src into @p dst, as much as fits with its terminator. The copy every
283 * row and line makes, without snprintf(): whatever the format, a call to
284 * snprintf() reaches newlib's float conversion, and the panel's call graph
285 * puts 1952 bytes of stack below it (tools/stack_check.py -v).
286 */
287SP_SHARED void sp_copy(char *dst, size_t n, const char *src)
288{
289 size_t i = 0;
290 while (src != NULL && i + 1u < n && src[i] != '\0') {
291 dst[i] = src[i];
292 ++i;
293 }
294 dst[i] = '\0';
295}
296
297/* The bytes of the next line of @p at that fit @p cells: up to the last
298 * space in it, or the whole width where no space falls after its start.
299 * A line holds one cell at least, so every line ends. */
300SP_SHARED size_t sp_line_len(const char *at, int cells)
301{
302 cells = (cells > 0) ? cells : 1;
303 if (gfx_text_cells(at) <= cells) {
304 return strlen(at);
305 }
306 const size_t len = gfx_text_prefix(at, cells);
307 size_t cut = len;
308 while (cut > 0u && at[cut] != ' ') {
309 --cut;
310 }
311 return (cut > 0u) ? cut : len;
312}
313
314/*
315 * @p text in lines of at most @p cells, broken after a space where one
316 * falls in the line and inside a word where none does, from (x, y) down by
317 * @p pitch. At most @p max_lines lines; the last of them ends in ".." when
318 * the text goes on. Returns the lines drawn. A manual step is the
319 * profile's own text and can be up to ESC_MANUAL_ACTION_MAX bytes, so it is
320 * wrapped, not cut to one line.
321 */
322SP_SHARED int sp_wrap(gfx_canvas_t *c, int x, int y, int pitch,
323 const char *text, int cells, int max_lines,
324 gfx_color_t ink)
325{
326 int lines = 0;
327 const char *at = (text != NULL) ? text : "";
328 while (*at == ' ') {
329 ++at;
330 }
331 while (*at != '\0' && lines < max_lines) {
332 char buf[192];
333 ++lines;
334 if (lines == max_lines) {
335 /* The last line takes the rest, cut with ".." if it is more
336 * than fits. */
337 sp_copy(buf, sizeof(buf), at);
338 sp_cut(buf, sizeof(buf), cells);
339 at += strlen(at);
340 } else {
341 /* The line, no further than the text's end; the copy no
342 * longer than the buffer. */
343 const size_t rest = strlen(at);
344 size_t len = sp_line_len(at, cells);
345 len = (len < rest) ? len : rest;
346 const size_t n = (len < sizeof(buf)) ? len : sizeof(buf) - 1u;
347 memcpy(buf, at, n);
348 buf[n] = '\0';
349 at += len;
350 /* len is at most strlen(at): at stops on the terminator. */
351 /* NOLINTNEXTLINE(clang-analyzer-security.ArrayBound) */
352 while (*at == ' ') {
353 ++at;
354 }
355 }
356 gfx_text(c, x, y, buf, SP_FONT, ink, 1);
357 y += pitch;
358 }
359 return lines;
360}
361
362/* The lines sp_wrap() would draw, without drawing them. */
363static inline int sp_wrap_lines(const char *text, int cells, int max_lines)
364{
365 int lines = 0;
366 const char *at = (text != NULL) ? text : "";
367 while (*at == ' ') {
368 ++at;
369 }
370 while (*at != '\0' && lines < max_lines) {
371 at += sp_line_len(at, cells);
372 while (*at == ' ') {
373 ++at;
374 }
375 ++lines;
376 }
377 return lines;
378}
379
390SP_SHARED void sp_note(gfx_canvas_t *c, int x, int right, const char *text,
391 gfx_color_t ink)
392{
393 const int cells = (right - x) / SP_CELL;
394 const int y = (sp_wrap_lines(text, cells, 2) > 1) ? BTN_Y + 6
395 : BTN_Y + (BTN_H - 20) / 2;
396 (void)sp_wrap(c, x, y, LINE_P, text, cells, 2, ink);
397}
398
399/* --- the panel's side, in esc_stick_panel.c ----------------------------- */
400
405void esc_stick_panel_reset(void);
406
408void esc_stick_panel_enter(void);
414void esc_stick_panel_leave(void);
419void esc_stick_panel_tick(float dt_s);
425void esc_stick_panel_cancel(void);
426
433bool esc_stick_panel_down(const touch_event_t *evt);
434
437bool esc_stick_panel_safe_at(int px, int py);
438
446bool esc_stick_panel_back(void);
447
454
461bool esc_stick_panel_holds_press(uint8_t id);
462
467void esc_stick_panel_open(void);
468
476uint32_t esc_stick_panel_showing(uint32_t h);
477
478/* The session's pages, on a revision not yet drawn: everything under
479 * PROG_FULL_Y. */
#define ROW_Y0
Definition analyser_screen.c:116
#define ROW_H
Definition analyser_screen.c:117
void esc_stick_panel_render(gfx_canvas_t *c)
Draws the page set by the stage: the manual steps, the prompt, the warning, the list or the profile's...
Definition esc_stick_draw.c:800
gfx: an RGB565 (16-bit red, green, blue) rasteriser with no dependencies.
void gfx_text_in(gfx_canvas_t *c, gfx_rect_t box, const char *s, const gfx_font_t *font, gfx_color_t fg, int scale, gfx_align_t align)
Definition gfx_text.c:316
size_t gfx_text_prefix(const char *s, int cells)
Definition gfx_text.c:77
int gfx_text_cells(const char *s)
Definition gfx_text.c:65
int gfx_text(gfx_canvas_t *c, int x, int y, const char *s, const gfx_font_t *font, gfx_color_t fg, int scale)
Definition gfx_text.c:286
The registry of programmers and runs: one static table that the PROGRAM (and later TEST) lists of a w...
Definition gfx.h:129
Definition gfx.h:81
Definition esc_stick_int.h:190
A DOWN, MOVE or UP of one contact.
Definition touch_types.h:69
Plain data types shared by the touch controller driver, the coordinate mapper and the event tracker....
SP_SHARED void widget_enum(gfx_canvas_t *c, int y, const char *text, gfx_color_t ink)
A row's value, right-aligned in the value column.
Definition esc_stick_int.h:254
#define SP_CELL
Definition esc_stick_int.h:47
uint32_t esc_stick_panel_showing(uint32_t h)
The panel's part of what decides which control lies under a point: the level, the pages' scrolls,...
Definition esc_stick_touch.c:220
void esc_stick_panel_open(void)
The session opens on the makers, from the top, the search as typed; TIMING and its keypad closed.
Definition esc_stick_panel.c:119
#define ROWS_MAX
Definition esc_stick_int.h:93
#define RUN_Y
Definition esc_stick_int.h:118
#define CTRL_W
Definition esc_stick_int.h:100
#define HELP_P
Definition esc_stick_int.h:119
SP_SHARED void draw_crumb(gfx_canvas_t *c, const char *trail)
The crumb row: BACK, and trail in SP_FONT_CRUMB beside it.
Definition esc_stick_int.h:239
void esc_stick_panel_reset(void)
The page set's init: the panel's state cleared and its controls laid out, TIMING's keypad closed.
Definition esc_stick_panel.c:77
#define CRUMB_Y
Definition esc_stick_int.h:67
#define CTRL_X
Definition esc_stick_int.h:101
void esc_stick_panel_enter(void)
The host's entry.
Definition esc_stick_panel.c:110
void esc_stick_panel_leave(void)
The host is left: a run ends, the warning, the search's keyboard and TIMING's keypad close,...
Definition esc_stick_panel.c:593
bool esc_stick_panel_down(const touch_event_t *evt)
A DOWN on the session's pages, in the walk of hit(). TIMING's keypad, while open, takes the DOWN and ...
Definition esc_stick_touch.c:336
#define ROW_P
Definition esc_stick_int.h:92
#define RUN_L0
Definition esc_stick_int.h:122
#define HELP_Y
Definition esc_stick_int.h:120
#define SP_FONT
The faces: SP_FONT, the type scale's LABEL face (10 x 20, cells of SP_CELL px), for every label,...
Definition esc_stick_int.h:46
#define ROW_TY
Definition esc_stick_int.h:95
#define BTN_H
Definition esc_stick_int.h:70
#define RUN_LINES
Definition esc_stick_int.h:123
void esc_stick_panel_follow(const touch_event_t *evt)
A MOVE or an UP: the search's keys, TIMING's keypad and the warning's hold follow it.
Definition esc_stick_touch.c:209
void esc_stick_panel_tick(float dt_s)
A frame.
Definition esc_stick_panel.c:510
bool esc_stick_panel_back(void)
The home tag in the session: TIMING's keypad closes as CANCEL does; otherwise what covers the page cl...
Definition esc_stick_touch.c:286
#define CARD_B
Definition esc_stick_int.h:75
#define LINE_P
Definition esc_stick_int.h:52
#define CRUMB_H
Definition esc_stick_int.h:68
bool esc_stick_panel_holds_press(uint8_t id)
Whether contact id holds the warning's hold, a key of the search's keyboard or a key of TIMING's keyp...
Definition esc_stick_touch.c:201
#define BTN_Y
The foot row's top: 4px over the alert band, BTN_H tall.
Definition esc_stick_int.h:72
#define RUN_H
Definition esc_stick_int.h:121
void esc_stick_panel_cancel(void)
Touches were lost: a press that may become a tap ends, a hold ends, a key held on the keyboard or the...
Definition esc_stick_panel.c:662
#define SP_FONT_CRUMB
Definition esc_stick_int.h:48
bool esc_stick_panel_safe_at(int px, int py)
Definition esc_stick_touch.c:321
SP_SHARED void sp_note(gfx_canvas_t *c, int x, int right, const char *text, gfx_color_t ink)
A note at the foot of a page, from x to right, left of the buttons there: on one line level with thei...
Definition esc_stick_int.h:390
The panel's geometry, in one place.
#define UI_BAND_H
The height of the band the router owns across the top, with STOP: 60px, 9.0mm.
Definition ui_layout.h:35
#define UI_ALERT_Y
The alert band's top in panel coordinates: UI_PANEL_H - UI_ALERT_H.
Definition ui_layout.h:47
Screen router.
The interface's words, one table per language.
The visual language of the bench, resolved at runtime.
Stateless drawing helpers shared by every screen.
void ui_button_font(gfx_canvas_t *c, gfx_rect_t r, const char *label, gfx_color_t fill, bool pressed, bool enabled, const gfx_font_t *f)
ui_button() with its label in face f.
Definition ui_widgets.c:89