ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ereader_ui_input.c
Go to the documentation of this file.
1
21
22#include <stddef.h>
23#include <stdint.h>
24#include <string.h>
25
26#include "er_pageturn.h"
27#include "ereader_ui_steps.h"
28#include "ra8_board_ek_ra8d2.h"
29#include "ra8_display_pal.h"
30#include "ra8_err.h"
31#include "ra8_gfx.h"
32#include "ra8_gfx_font.h"
33#include "ra8_i3c.h"
34#include "ra8_keyboard.h"
35#include "ra8_port_constants.h"
36#include "ra8_port_utils.h"
37#include "ra8_touch.h"
38#include "ra8_ui.h"
39#include "ra8_widget.h"
40
41/* ===========================================================================
42 * On-screen keyboard -- built with ra8_keyboard, rendered with ra8_gfx
43 * =========================================================================== */
44
46typedef enum : int32_t {
54 /* Glyph-icon geometry (px from the key centre) for SHIFT / DEL / RETURN. */
68
70typedef enum : uint32_t {
71 k_er_kbd_bg = 0xCCCCCCU,
72 k_er_kbd_keylt = 0xFFFFFFU,
73 k_er_kbd_keydk = 0xAAAAAAU,
74 k_er_kbd_keysh = 0x909090U,
76
82static const char* er_kbd_label(uint8_t idx, char* s)
83{
84 switch (s_kb.keys[idx].kind) {
86 return "space";
88 if (s_kb.keys[idx].aux == (uint8_t)k_ra8_kbd_layer_numbers) {
89 return "123";
90 }
91 if (s_kb.keys[idx].aux == (uint8_t)k_ra8_kbd_layer_symbols) {
92 return "#+=";
93 }
94 return "ABC";
95 default:
96 s[0] = ra8_kbd_key_glyph(&s_kb, idx);
97 s[1] = '\0';
98 return s;
99 }
100}
101
103static void er_round_rect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t c)
104{
105 const int32_t r = (int32_t)k_er_kbd_radius;
106 (void)ra8_gfx_rect(x + r, y, w - (2 * r), h, c, true);
107 (void)ra8_gfx_rect(x, y + r, w, h - (2 * r), c, true);
108 (void)ra8_gfx_circle(x + r, y + r, r, c, true);
109 (void)ra8_gfx_circle((x + w - r) - 1, y + r, r, c, true);
110 (void)ra8_gfx_circle(x + r, (y + h - r) - 1, r, c, true);
111 (void)ra8_gfx_circle((x + w - r) - 1, (y + h - r) - 1, r, c, true);
112}
113
115static void er_tri_up(int32_t cx, int32_t ay, int32_t by, int32_t hw, uint32_t c)
116{
117 const int32_t span = by - ay;
118 for (int32_t y = ay; y <= by; ++y) {
119 const int32_t half = (span > 0) ? ((hw * (y - ay)) / span) : hw;
120 (void)ra8_gfx_line(cx - half, y, cx + half, y, c);
121 }
122}
123
125static void er_icon_shift(int32_t cx, int32_t cy, uint32_t c)
126{
127 er_tri_up(cx, cy - (int32_t)k_er_ico_atop, cy - (int32_t)k_er_ico_abot, (int32_t)k_er_ico_aw, c);
128 (void)ra8_gfx_rect(cx - (int32_t)k_er_ico_sw,
129 cy - (int32_t)k_er_ico_abot,
130 2 * (int32_t)k_er_ico_sw,
131 (int32_t)k_er_ico_abot + (int32_t)k_er_ico_sbot,
132 c,
133 true);
134}
135
137static void er_icon_delete(int32_t cx, int32_t cy, uint32_t c)
138{
139 const int32_t w = (int32_t)k_er_ico_dw;
140 const int32_t h = (int32_t)k_er_ico_dh;
141 const int32_t tx = (cx - w) + h; /* where the tip opens into the body */
142 (void)ra8_gfx_line(cx - w, cy, tx, cy - h, c);
143 (void)ra8_gfx_line(tx, cy - h, cx + w, cy - h, c);
144 (void)ra8_gfx_line(cx + w, cy - h, cx + w, cy + h, c);
145 (void)ra8_gfx_line(cx + w, cy + h, tx, cy + h, c);
146 (void)ra8_gfx_line(tx, cy + h, cx - w, cy, c);
147 const int32_t xo = (int32_t)k_er_ico_dxo;
148 const int32_t xe = (int32_t)k_er_ico_dx;
149 (void)ra8_gfx_line((cx + xo) - xe, cy - xe, cx + xo + xe, cy + xe, c);
150 (void)ra8_gfx_line(cx + xo + xe, cy - xe, (cx + xo) - xe, cy + xe, c);
151}
152
154static void er_icon_return(int32_t cx, int32_t cy, uint32_t c)
155{
156 const int32_t w = (int32_t)k_er_ico_rw;
157 const int32_t a = (int32_t)k_er_ico_rah;
158 (void)ra8_gfx_line(cx + w, cy - (int32_t)k_er_ico_rh, cx + w, cy, c);
159 (void)ra8_gfx_line(cx + w, cy, cx - w, cy, c);
160 (void)ra8_gfx_line(cx - w, cy, (cx - w) + a, cy - a, c);
161 (void)ra8_gfx_line(cx - w, cy, (cx - w) + a, cy + a, c);
162}
163
166{
167 return (kind != k_ra8_kbd_key_char) && (kind != k_ra8_kbd_key_space);
168}
169
171static void
172er_draw_key_label(uint8_t idx, int32_t kx, int32_t ky, int32_t kw, int32_t kh, uint32_t fill)
173{
174 char scratch[k_er_kbd_lab_max] = {};
175 const char* lab = er_kbd_label(idx, scratch);
176 const int32_t lw = (int32_t)strlen(lab) * (int32_t)k_er_kbd_glyph_w;
177 const int32_t lx = kx + ((kw - lw) / 2);
178 const int32_t ly = ky + ((kh - (int32_t)k_er_kbd_glyph_h) / 2);
179 (void)ra8_gfx_text_out(lx, ly, lab, &ra8_gfx_font_8x16, (uint32_t)k_er_ink, fill);
180}
181
188static void er_draw_key(uint8_t idx)
189{
190 const ra8_ui_rect_t r = s_kb.keys[idx].rect;
191 const int32_t g = (int32_t)k_er_kbd_gap;
192 const int32_t kx = r.x + g;
193 const int32_t ky = r.y + g;
194 const int32_t kw = r.w - (2 * g);
195 const int32_t kh = r.h - (2 * g);
196 const ra8_kbd_key_kind_t kind = s_kb.keys[idx].kind;
197 const bool special = er_key_is_special(kind);
198 const bool sh_on = (kind == k_ra8_kbd_key_shift) && s_kb.shift;
199 const uint32_t fill = (special && !sh_on) ? (uint32_t)k_er_kbd_keydk : (uint32_t)k_er_kbd_keylt;
200 er_round_rect(kx, ky + (int32_t)k_er_kbd_shadow_dy, kw, kh, (uint32_t)k_er_kbd_keysh);
201 er_round_rect(kx, ky, kw, kh, fill);
202
203 const int32_t cx = kx + (kw / 2);
204 const int32_t cy = ky + (kh / 2);
205 switch (kind) {
207 er_icon_shift(cx, cy, (uint32_t)k_er_ink);
208 break;
210 er_icon_delete(cx, cy, (uint32_t)k_er_ink);
211 break;
213 er_icon_return(cx, cy, (uint32_t)k_er_ink);
214 break;
215 default:
216 er_draw_key_label(idx, kx, ky, kw, kh, fill);
217 break;
218 }
219}
220
223{
224 (void)w;
225 const int32_t qy = (int32_t)k_er_statusbar_h + (int32_t)k_er_text_pad;
226 er_text_left((int32_t)k_er_pad_ui, qy, "Search:", (uint32_t)k_er_ink_muted);
227 const char* shown = (s_query.len > 0U) ? s_query.buf : k_er_search_hint;
228 er_text_left((int32_t)k_er_pad_ui + (int32_t)k_er_kbd_qlabel, qy, shown, (uint32_t)k_er_ink);
229}
230
233{
234 (void)w;
235 /* Fill the keyboard band with the iOS-style gray backdrop. */
236 const int32_t band_y = (int32_t)k_er_statusbar_h + (int32_t)k_er_toolbar_h;
237 (void)ra8_gfx_rect(0,
238 band_y,
239 (int32_t)s_fb.width_px,
240 (int32_t)s_fb.height_px - band_y,
241 (uint32_t)k_er_kbd_bg,
242 true);
243
244 const int32_t ky = band_y + (int32_t)k_er_pad_ui;
245 const ra8_ui_rect_t frame = {.x = (int32_t)k_er_pad_ui,
246 .y = ky,
247 .w = (int32_t)s_fb.width_px - (2 * (int32_t)k_er_pad_ui),
248 .h = (int32_t)s_fb.height_px - ky - (int32_t)k_er_pad_ui};
249 (void)ra8_kbd_layout_init(&s_kb, &frame);
250
251 s_target_count = 0U;
252 for (uint8_t i = 0U; i < s_kb.count; i++) {
253 er_draw_key(i);
254 if (s_target_count < (uint16_t)k_er_max_targets) {
255 s_targets[s_target_count].rect = s_kb.keys[i].rect;
256 s_targets[s_target_count].action_id = (uint16_t)((uint16_t)k_er_act_key_base + (uint16_t)i);
257 s_targets[s_target_count].reserved = 0U;
259 }
260 }
261}
262
266
268{
269 (void)ra8_gfx_clear((uint32_t)k_er_paper);
270 const ra8_ui_rect_t frame = {0, 0, (int32_t)s_fb.width_px, (int32_t)s_fb.height_px};
271 ra8_widget_t bands[2] = {};
272 bands[0].vt = &k_er_kbd_search_vt;
273 bands[0].fixed = (int16_t)((int32_t)k_er_statusbar_h + (int32_t)k_er_toolbar_h);
274 bands[0].visible = true;
275 bands[1].vt = &k_er_kbd_keys_vt;
276 bands[1].flex = 1U;
277 bands[1].visible = true;
278 ra8_box_t band_scratch[3];
279 (void)ra8_widget_layout_stack(bands, 2U, &frame, k_ra8_widget_axis_col, 0, 0, band_scratch, 3U);
280 for (uint16_t i = 0U; i < 2U; ++i) {
282 }
283 (void)ra8_widget_render_dirty(bands, 2U);
284}
285
286/* ===========================================================================
287 * Low-battery nag banner + fuel-gauge polling
288 * =========================================================================== */
289
291static const char k_er_nag_low_msg[] = "Low battery ";
292static const char k_er_nag_crit_msg[] = "Battery critical ";
294static const char k_er_nag_hint[] = " (tap to dismiss)";
295
297static bool er_fg_read(uint8_t reg, uint8_t* out)
298{
299 if (out == nullptr) {
300 return false;
301 }
302 if (ra8_i3c_write((uint8_t)k_er_touch_channel, (uint8_t)k_er_fg_addr, &reg, 1U, true) !=
303 k_ra8_ok) {
304 return false;
305 }
306 return (ra8_i3c_read((uint8_t)k_er_touch_channel, (uint8_t)k_er_fg_addr, out, 1U, false) ==
307 k_ra8_ok);
308}
309
311static bool er_read_battery(uint8_t* soc, bool* charging)
312{
313 if ((soc == nullptr) || (charging == nullptr)) {
314 return false;
315 }
316 uint8_t pct = 0U;
317 uint8_t crate_hi = 0U;
318 if (!er_fg_read((uint8_t)k_er_fg_reg_soc, &pct)) {
319 return false;
320 }
321 if (!er_fg_read((uint8_t)k_er_fg_reg_crate, &crate_hi)) {
322 return false;
323 }
324 *soc = pct;
325 *charging = ((crate_hi & (uint8_t)k_er_fg_sign) == 0U);
326 return true;
327}
328
330static void er_append_pct(char* buf, uint32_t cap, uint32_t* pos, uint8_t v)
331{
332 char tmp[k_er_nag_dec_max];
333 uint32_t n = 0U;
334 if (v == 0U) {
335 tmp[n] = '0';
336 n++;
337 }
338 while ((v > 0U) && (n < (uint32_t)k_er_nag_dec_max)) {
339 tmp[n] = (char)('0' + (v % (uint8_t)k_er_nag_dec_ten));
340 n++;
341 v = (uint8_t)(v / (uint8_t)k_er_nag_dec_ten);
342 }
343 for (uint32_t i = 0U; (i < n) && (*pos < (cap - 1U)); i++) {
344 buf[*pos] = tmp[n - 1U - i];
345 (*pos)++;
346 }
347 if (*pos < (cap - 1U)) {
348 buf[*pos] = '%';
349 (*pos)++;
350 }
351 buf[*pos] = '\0';
352}
353
355static void er_append_str(char* buf, uint32_t cap, uint32_t* pos, const char* s)
356{
357 /* Bounded by the buffer cap (NASA Rule 2); the NUL is the early exit. */
358 for (uint32_t i = 0U; (i < (uint32_t)k_er_nag_line_max) && (s[i] != '\0') && (*pos < (cap - 1U));
359 i++) {
360 buf[*pos] = s[i];
361 (*pos)++;
362 }
363 buf[*pos] = '\0';
364}
365
367static bool er_nag_hit(int32_t x, int32_t y)
368{
369 const int32_t bx = (int32_t)k_er_nag_margin;
370 const int32_t bw = (int32_t)s_fb.width_px - (2 * (int32_t)k_er_nag_margin);
371 return (x >= bx) && (x < (bx + bw)) && (y >= (int32_t)k_er_nag_top) &&
372 (y < ((int32_t)k_er_nag_top + (int32_t)k_er_nag_h));
373}
374
376{
377 (void)w;
379 return;
380 }
381 const bool crit = (s_batt_nag == k_ra8_batt_nag_critical);
382 const uint32_t bg = crit ? (uint32_t)k_er_nag_crit_bg : (uint32_t)k_er_nag_low_bg;
383 const int32_t x = (int32_t)k_er_nag_margin;
384 const int32_t bw = (int32_t)s_fb.width_px - (2 * (int32_t)k_er_nag_margin);
385 (void)ra8_gfx_rect(x, (int32_t)k_er_nag_top, bw, (int32_t)k_er_nag_h, bg, true);
386 (void)ra8_gfx_rect(x, (int32_t)k_er_nag_top, bw, (int32_t)k_er_nag_h, (uint32_t)k_er_ink, false);
387 char line[k_er_nag_line_max];
388 uint32_t pos = 0U;
389 er_append_str(line,
390 (uint32_t)k_er_nag_line_max,
391 &pos,
393 er_append_pct(line, (uint32_t)k_er_nag_line_max, &pos, s_batt_soc);
394 er_append_str(line, (uint32_t)k_er_nag_line_max, &pos, k_er_nag_hint);
395 (void)ra8_gfx_text_out(x + (int32_t)k_er_nag_text_dx,
396 (int32_t)k_er_nag_top + (int32_t)k_er_nag_text_dy,
397 line,
399 (uint32_t)k_er_nag_text,
400 bg);
401}
402
403/* ===========================================================================
404 * Tap routing -- per-screen dispatch shared by touch + buttons
405 * =========================================================================== */
406
426static bool er_handle_reading_tap(int32_t x, int32_t y)
427{
428 const ra8_ui_rect_t back = {0, 0, (int32_t)k_er_back_w, (int32_t)k_er_statusbar_h};
429 if (ra8_ui_rect_contains(&back, x, y)) {
430 /* Back returns from a link jump (footnote or chapter) first, then pops the
431 * screen back to the Library (#110). */
432 if (s_loc_back_count > 0U) {
436 return true;
437 }
438 uint16_t prev = 0U;
439 return (ra8_ui_nav_pop(&s_nav, &prev) == k_ra8_ok);
440 }
441 /* Follow an in-content `<a>` link before falling back to page-turn. */
442 if (er_reading_link_tap(x, y)) {
443 return true;
444 }
445 /* Page-turn: a tap in the right third of the screen advances, the left third
446 * goes back, the middle third is neutral. Crosses chapter boundaries and
447 * clamps at the book ends (er_apply_pageturn). A no-op leaves the screen
448 * unchanged (e.g. in the bitmap fallback where s_reading_pages stays 1). */
449 return er_apply_pageturn(er_tap_to_dir(x, (int32_t)s_fb.width_px));
450}
451
460static bool er_handle_keyboard_tap(int32_t x, int32_t y)
461{
462 uint16_t action = (uint16_t)k_er_act_none;
463 bool hit = false;
464 (void)ra8_ui_hit_test(s_targets, s_target_count, x, y, &action, &hit);
465 if (!hit || (action < (uint16_t)k_er_act_key_base)) {
466 return false;
467 }
468 const uint8_t idx = (uint8_t)(action - (uint16_t)k_er_act_key_base);
469 (void)ra8_kbd_apply(&s_query, &s_kb, idx);
470 if (s_query.committed) {
471 uint16_t prev = (uint16_t)k_er_screen_library;
472 return (ra8_ui_nav_pop(&s_nav, &prev) == k_ra8_ok); /* ENTER -> filtered Library */
473 }
474 return true; /* re-render to show the updated query */
475}
476
496static bool er_handle_tap(int32_t x, int32_t y)
497{
498 /* Default any screen/location change to a clean full refresh; a same-chapter
499 * page turn lowers this to a fast partial inside er_apply_pageturn. */
501 s_nag_region_only = false; /* assume a full repaint unless only the nag changes. */
502 /* A tap on the low-battery banner dismisses it (acknowledge). ra8_batt will not
503 * re-raise the same band until the battery recovers, so it stays dismissed
504 * until the next descent into a worse band, or a recovery then re-drain. Only
505 * the banner rect changed, so the caller can repaint just that region. */
506 if ((s_batt_nag != k_ra8_batt_nag_none) && er_nag_hit(x, y)) {
508 s_nag_region_only = true;
509 return true;
510 }
511 uint16_t top = (uint16_t)k_er_screen_library;
512 (void)ra8_ui_nav_top(&s_nav, &top);
513 if (top == (uint16_t)k_er_screen_reading) {
514 return er_handle_reading_tap(x, y);
515 }
516 if (top == (uint16_t)k_er_screen_keyboard) {
517 return er_handle_keyboard_tap(x, y);
518 }
519#ifdef RA8_APP_SETTINGS
520 if (top == (uint16_t)k_er_screen_settings) {
521 /* Settings has no tap targets; any tap returns to the Library. */
522 return (ra8_ui_nav_pop(&s_nav, &top) == k_ra8_ok);
523 }
524#endif
525 uint16_t action = (uint16_t)k_er_act_none;
526 bool hit = false;
527 (void)ra8_ui_hit_test(s_targets, s_target_count, x, y, &action, &hit);
528#ifdef RA8_APP_SETTINGS
529 if (hit && (action == (uint16_t)k_er_act_settings)) {
530 return (ra8_ui_nav_push(&s_nav, (uint16_t)k_er_screen_settings) == k_ra8_ok);
531 }
532#endif
533 if (hit && (action == (uint16_t)k_er_act_open_book)) {
534 s_reading_page = 0U; /* always open a book at its first page */
535 s_chapter_idx = 0U; /* and its first chapter */
536 s_loc_back_count = 0U; /* with a fresh navigation back-stack */
537 s_pending_event = k_display_event_open; /* fresh book -> clean INIT */
538 return (ra8_ui_nav_push(&s_nav, (uint16_t)k_er_screen_reading) == k_ra8_ok);
539 }
540 if (hit && (action == (uint16_t)k_er_act_search)) {
541 (void)ra8_kbd_text_init(&s_query); /* fresh query each time Search opens */
542 return (ra8_ui_nav_push(&s_nav, (uint16_t)k_er_screen_keyboard) == k_ra8_ok);
543 }
544 return false;
545}
546
547/* ===========================================================================
548 * Per-frame input polling -- touch, page-turn buttons, fuel gauge
549 * =========================================================================== */
550
552static bool s_was_touching;
553
555static bool s_was_sw1;
556static bool s_was_sw2;
557
559{
560 ra8_touch_point_t pt = {};
561 uint8_t got = 0U;
562 if (ra8_touch_read(&pt, (uint8_t)k_er_touch_max_points, &got) != k_ra8_ok) {
563 return;
564 }
565 const bool touching = (got > 0U);
566 if (touching && !s_was_touching) {
567 if (er_handle_tap((int32_t)pt.x, (int32_t)pt.y)) {
568 if (s_nag_region_only) {
569 er_render_nag_region(); /* a tap that only dismissed the banner */
570 } else {
572 }
574 }
575 }
576 s_was_touching = touching;
577}
578
581{
583 if (ra8_gpio_read(pin, &level) != k_ra8_ok) {
584 return false; /* unreadable switch -> treat as released (touch still works) */
585 }
586 return (level == k_ra8_level_low);
587}
588
590{
593 (void)ra8_board_sw_pin(k_ra8_board_sw1, &sw1_pin);
594 (void)ra8_board_sw_pin(k_ra8_board_sw2, &sw2_pin);
595 const bool sw1 = er_sw_pressed(sw1_pin);
596 const bool sw2 = er_sw_pressed(sw2_pin);
597 const bool fresh1 = sw1 && !s_was_sw1;
598 const bool fresh2 = sw2 && !s_was_sw2;
599 s_was_sw1 = sw1;
600 s_was_sw2 = sw2;
601 /* Page-turn buttons act only in the Reading view (a press elsewhere is a
602 * no-op, so it cannot disturb the reading location from another screen). */
603 uint16_t top = (uint16_t)k_er_screen_library;
604 (void)ra8_ui_nav_top(&s_nav, &top);
605 if ((top != (uint16_t)k_er_screen_reading) || !(fresh1 || fresh2)) {
606 return;
607 }
608 if (er_apply_pageturn(er_buttons_to_dir(fresh1, fresh2))) {
611 }
612}
613
627{
628 /* Throttle: the gauge moves slowly and each read is two I2C transactions, so
629 * polling it every frame dominated the loop (the GT911 touch read is the only
630 * other per-frame I2C). Poll ~once a second so touch stays responsive. */
631 static uint32_t s_skip = 0U;
632 if (s_skip != 0U) {
633 s_skip--;
634 return;
635 }
636 s_skip = (uint32_t)k_er_batt_every - 1U;
637
638 uint8_t soc = 0U;
639 bool chg = false;
640 if (!er_read_battery(&soc, &chg)) {
641 return; /* best-effort: no gauge fitted -> no banner */
642 }
643 s_batt_soc = soc;
645 if (ra8_batt_update(&s_batt_mon, soc, chg, &nag) != k_ra8_ok) {
646 return;
647 }
648 const ra8_batt_nag_t prev = s_batt_nag;
649 const uint8_t recover = (uint8_t)((uint8_t)k_ra8_batt_low_pct + (uint8_t)k_ra8_batt_rearm_margin);
650 if (nag != k_ra8_batt_nag_none) {
651 s_batt_nag = nag; /* new low / critical edge -> raise or upgrade the banner */
652 } else if ((s_batt_nag != k_ra8_batt_nag_none) && (chg || (soc > recover))) {
653 s_batt_nag = k_ra8_batt_nag_none; /* recovered or charging -> clear the banner */
654 }
655 if (s_batt_nag != prev) {
656 er_render_nag_region(); /* only the banner changed -> repaint just its rect */
658 }
659}
Pure page-turn + input-region decisions for the e-reader (#78).
static er_dir_t er_buttons_to_dir(bool sw1_pressed, bool sw2_pressed)
Decide a page-turn direction from the two user-button states.
Definition er_pageturn.h:98
static er_dir_t er_tap_to_dir(int32_t x, int32_t width)
Decide a page-turn direction from a touch X coordinate (edge taps).
Definition er_pageturn.h:67
static bool er_fg_read(uint8_t reg, uint8_t *out)
Best-effort read of one fuel-gauge register on the touch IIC_B bus.
static const char k_er_nag_hint[]
Trailing hint so the banner says how to close itself.
static void er_kbd_search_render(ra8_widget_t *w)
Search-field band widget: the "Search:" label + the query / hint.
void er_poll_buttons(void)
Poll SW1/SW2 and turn a page on a fresh press (#78).
static bool er_handle_keyboard_tap(int32_t x, int32_t y)
Route a tap on the keyboard screen: type a key, or commit on ENTER.
static void er_append_pct(char *buf, uint32_t cap, uint32_t *pos, uint8_t v)
Append "<v>%" (v in 0..255) to buf at *pos, bounded + NUL-terminated.
static void er_draw_key(uint8_t idx)
Draw key idx Apple-style: a shadowed rounded key + glyph icon / label.
static const char * er_kbd_label(uint8_t idx, char *s)
Text label for key idx (SPACE / 123-ABC, or live-case char in s).
static void er_kbd_keys_render(ra8_widget_t *w)
Keyboard band widget: gray backdrop + the laid-out keys + tap targets.
static void er_tri_up(int32_t cx, int32_t ay, int32_t by, int32_t hw, uint32_t c)
Filled upward triangle from apex row ay to base row by (scanlines).
static bool er_sw_pressed(ra8_port_pin_t pin)
Read a user switch (active-low); true == pressed.
static const ra8_widget_vtable_t k_er_kbd_keys_vt
void er_nag_render(ra8_widget_t *w)
Nag banner overlay widget: a centered low-battery toast over the screen.
static void er_icon_delete(int32_t cx, int32_t cy, uint32_t c)
BACKSPACE glyph: a left-pointing pentagon outline with an X inside.
static bool er_handle_tap(int32_t x, int32_t y)
Route a tap to a navigation action for the current screen.
static const char k_er_nag_crit_msg[]
static void er_icon_return(int32_t cx, int32_t cy, uint32_t c)
RETURN glyph: a carriage-return arrow (tail down, shaft left, arrowhead).
static bool er_read_battery(uint8_t *soc, bool *charging)
Read SOC + charge direction from the fuel gauge; false if absent (NAK).
void er_poll_battery(void)
Implementation of er_poll_battery() – the clear-the-banner glue.
static bool er_key_is_special(ra8_kbd_key_kind_t kind)
True for keys drawn dark (SHIFT / BACKSPACE / 123-ABC / RETURN).
static bool er_handle_reading_tap(int32_t x, int32_t y)
Handle a tap while the Reading view is on top.
static const char k_er_nag_low_msg[]
Nag banner messages (the SOC percent + dismiss hint follow at render).
static const ra8_widget_vtable_t k_er_kbd_search_vt
Vtables for the keyboard search-field / keys band widgets.
static bool er_nag_hit(int32_t x, int32_t y)
True iff (x,y) lands on the low-battery banner rect.
er_kbd_render_t
Keyboard chrome metrics (8x16 glyphs + keys).
@ k_er_ico_dw
Delete glyph half-width.
@ k_er_kbd_glyph_h
ra8_gfx_font_8x16 glyph height.
@ k_er_kbd_glyph_w
ra8_gfx_font_8x16 glyph width.
@ k_er_kbd_gap
Inset gap around each key.
@ k_er_ico_rw
Return shaft half-width.
@ k_er_kbd_shadow_dy
Key drop-shadow offset (down).
@ k_er_ico_dx
Delete X half-extent.
@ k_er_ico_dh
Delete glyph half-height.
@ k_er_ico_rh
Return tail height.
@ k_er_ico_rah
Return arrowhead leg.
@ k_er_kbd_qlabel
"Search:" label column width.
@ k_er_kbd_radius
Key corner radius (rounded, iOS).
@ k_er_ico_sbot
Shift stem, below centre.
@ k_er_ico_abot
Shift arrowhead base, above centre.
@ k_er_kbd_lab_max
Key-label scratch size.
@ k_er_ico_aw
Shift arrowhead half-width.
@ k_er_ico_sw
Shift stem half-width.
@ k_er_ico_atop
Shift arrowhead apex, above centre.
@ k_er_ico_dxo
Delete X right-of-centre offset.
static void er_round_rect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t c)
Filled rounded rect (rect cross + four corner discs).
er_kbd_color_t
Apple-style grayscale keyboard palette.
@ k_er_kbd_keydk
Dark (special) key.
@ k_er_kbd_bg
Keyboard background.
@ k_er_kbd_keysh
Key drop shadow.
@ k_er_kbd_keylt
Light (letter/space) key.
void er_render_keyboard(void)
Render the on-screen keyboard screen + collect its key targets.
static void er_draw_key_label(uint8_t idx, int32_t kx, int32_t ky, int32_t kw, int32_t kh, uint32_t fill)
Draw the text label of key idx centred in the key body.
void er_poll_touch(void)
Poll the touch controller and dispatch a tap on a fresh press.
static void er_icon_shift(int32_t cx, int32_t cy, uint32_t c)
SHIFT glyph: an up-arrow (filled arrowhead over a narrow stem).
static void er_append_str(char *buf, uint32_t cap, uint32_t *pos, const char *s)
Append NUL-terminated s to buf at *pos, bounded + NUL-terminated.
E-reader UI chrome – shared contract between main.c and the per-aspect helper translation units (scre...
@ k_er_paper
Page background (g15).
@ k_er_ink
Primary text / glyphs (g0).
@ k_er_ink_muted
Secondary text / metadata (g5).
const char k_er_search_hint[]
Search placeholder.
Definition main.c:84
ra8_ui_target_t s_targets[]
Tap targets for the current screen.
Definition main.c:217
ra8_kbd_layout_t s_kb
On-screen keyboard grid.
Definition main.c:223
ra8_kbd_text_t s_query
Live search query.
Definition main.c:226
uint32_t s_chapter_idx
Reading chapter index.
Definition main.c:178
uint16_t s_target_count
Tap targets currently populated.
Definition main.c:220
bool s_nag_region_only
True when a tap only toggled the nag.
Definition main.c:241
@ k_er_fg_reg_crate
CRATE register (sign = charge dir).
@ k_er_fg_addr
Fuel gauge 7-bit I2C address.
@ k_er_fg_sign
CRATE high-byte sign bit (discharge).
@ k_er_fg_reg_soc
SOC register (high byte = percent).
@ k_er_back_w
Reading back-region width (px).
@ k_er_max_targets
Max collected tap targets.
@ k_er_pad_ui
Side padding for bands / grid.
@ k_er_text_pad
Inset for text inside a box.
@ k_er_toolbar_h
Library toolbar band height.
@ k_er_statusbar_h
Top status-bar band height.
@ k_er_touch_channel
The board's GT911 bus.
@ k_er_touch_max_points
One contact = one tap.
display_turn_event_t s_pending_event
Pending refresh event for next flush.
Definition main.c:251
void er_flush_event(display_turn_event_t event)
Flush the panel for a refresh event through the cadence policy (#78).
Definition main.c:623
ra8_batt_monitor_t s_batt_mon
Low-battery nag policy state.
Definition main.c:235
@ k_er_nag_margin
Banner inset from the screen sides.
@ k_er_nag_h
Banner height (px).
@ k_er_nag_top
Banner top y (clear of the status bar).
@ k_er_nag_dec_max
Scratch for a "NNN%" decimal tail.
@ k_er_nag_text_dy
Text inset from the banner top.
@ k_er_nag_line_max
Banner text buffer size (bounds copy).
@ k_er_nag_text_dx
Text inset from the banner left.
@ k_er_nag_dec_ten
Decimal base.
@ k_er_batt_every
Poll the gauge every Nth frame (~1/s).
uint32_t s_loc_back_count
Entries used in s_loc_back.
Definition main.c:268
er_loc_t s_loc_back[]
Reading back-stack for link jumps.
Definition main.c:265
ra8_batt_nag_t s_batt_nag
Nag currently shown over the chrome.
Definition main.c:237
uint32_t s_reading_page
Reading current reflow page.
Definition main.c:229
void er_render_current(void)
Render whichever screen is on top of the navigation stack.
Definition main.c:585
@ k_er_act_none
Not a tap target.
@ k_er_act_open_book
Library card -> open the reading view.
@ k_er_act_settings
Bottom-nav Settings -> Settings app.
@ k_er_act_search
Toolbar Search -> open the keyboard.
@ k_er_act_key_base
Keyboard key i -> base + i.
void er_text_left(int32_t x, int32_t y, const char *str, uint32_t color)
Draw a left-anchored text run in the bundled font.
bool er_reading_link_tap(int32_t x, int32_t y)
Follow an in-content <a> tap in the Reading body (#110).
@ k_er_screen_reading
Reading view.
@ k_er_screen_keyboard
On-screen keyboard (search entry).
@ k_er_screen_library
Library / home grid.
@ k_er_screen_settings
Settings (optional app, if RA8_APP_SETTINGS).
bool er_apply_pageturn(er_dir_t dir)
Apply a page turn to the reading location (touch + button shared path).
uint8_t s_batt_soc
Last fuel-gauge SOC percent.
Definition main.c:239
void er_render_nag_region(void)
Repaint only the low-battery banner rect (dirty-region update).
Definition main.c:602
@ k_er_nag_low_bg
Low-battery banner fill (g5).
@ k_er_nag_text
Banner text (paper on the fill).
@ k_er_nag_crit_bg
Critical banner fill (g0 ink).
static display_fb_t s_fb
Definition main.c:203
static bool s_was_touching
Debounce: true while a contact is held (fire once per press).
Definition main.c:280
static bool s_was_sw1
Edge-detect: was SW1 held on the previous button poll.
Definition main.c:308
static bool s_was_sw2
Edge-detect: was SW2 held on the previous button poll.
Definition main.c:310
static ra8_app_nav_t s_nav
Navigation back-stack layered over s_reg.
Definition main.c:236
@ k_ra8_batt_low_pct
Low-battery threshold (inclusive).
Definition ra8_batt.h:69
@ k_ra8_batt_rearm_margin
Hysteresis above a band to re-arm it.
Definition ra8_batt.h:71
ra8_err_t ra8_batt_update(ra8_batt_monitor_t *mon, uint8_t soc_pct, bool charging, ra8_batt_nag_t *out_nag)
Fold one SOC reading into the monitor and report the nag to raise.
Definition ra8_batt.c:36
ra8_batt_nag_t
The low-battery warning a single ra8_batt_update step raises.
Definition ra8_batt.h:54
@ k_ra8_batt_nag_none
No new warning this step.
Definition ra8_batt.h:55
@ k_ra8_batt_nag_critical
SOC fell to the critical band (<=10%).
Definition ra8_batt.h:57
Board-support layer for the Renesas EK-RA8D2 v1 evaluation kit.
ra8_err_t ra8_board_sw_pin(ra8_board_sw_id_t sw, ra8_port_pin_t *out_pin)
Translate a board switch id into its underlying ra8_port_pin_t.
@ k_ra8_board_sw1
SW1, P009, IRQ13-DS (jumper E31).
@ k_ra8_board_sw2
SW2, P008, IRQ12-DS (jumper E32).
ra8_board_eth_pin_t pin
Pin.
Display Platform Abstraction Layer for the RA8D2.
@ k_display_event_chapter
Chapter-boundary turn – wants a clean refresh.
@ k_display_event_open
Book/first page opened – wants a clean INIT.
Error Code Definitions for ra8-firmware.
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
size_t strlen(const char *s)
Calculate string length.
Software 2D graphics primitives layered on top of a caller-ownedframebuffer (DRW / D/AVE 2D / GLCDC r...
ra8_err_t ra8_gfx_circle(int32_t cx, int32_t cy, int32_t r, uint32_t color, bool filled)
Draw a circle using midpoint algorithm.
ra8_err_t ra8_gfx_line(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t color)
Draw a line from (x0,y0) to (x1,y1) with Bresenham's algorithm.
ra8_err_t ra8_gfx_clear(uint32_t color)
Fill the bound framebuffer's clip region with a single colour.
ra8_err_t ra8_gfx_text_out(int32_t x, int32_t y, const char *str, const ra8_gfx_font_t *font, uint32_t fg_color, uint32_t bg_color)
Render a NUL-terminated ASCII string.
ra8_err_t ra8_gfx_rect(int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color, bool filled)
Draw an axis-aligned rectangle.
Bitmap font descriptor + bundled font handles for ra8_gfx.
const ra8_gfx_font_t ra8_gfx_font_8x16
Bundled 8x16 IBM PC VGA bitmap font, ASCII 0x20..0x7E.
I3C Bus Interface driver – unified native-I3C + I2C-compat modes.
ra8_err_t ra8_i3c_read(uint8_t channel, uint8_t addr, uint8_t *buf, uint32_t len, bool restart)
Read len bytes from addr.
Definition ra8_i3c.c:666
ra8_err_t ra8_i3c_write(uint8_t channel, uint8_t addr, const uint8_t *data, uint32_t len, bool restart)
Write len bytes to addr.
Definition ra8_i3c.c:621
On-screen keyboard widget – iOS-style layers, shift, hit-test.
ra8_err_t ra8_kbd_text_init(ra8_kbd_text_t *t)
Reset a text buffer to empty / uncommitted.
ra8_err_t ra8_kbd_apply(ra8_kbd_text_t *t, ra8_kbd_layout_t *kb, uint8_t key_idx)
Apply key key_idx to the text buffer, SHIFT, and active layer.
ra8_kbd_key_kind_t
What a key does when tapped.
@ k_ra8_kbd_key_space
Append a space.
@ k_ra8_kbd_key_layer
Switch to the layer in aux.
@ k_ra8_kbd_key_enter
Commit the query (RETURN).
@ k_ra8_kbd_key_char
Append the shift-correct char.
@ k_ra8_kbd_key_shift
Toggle the one-shot SHIFT.
@ k_ra8_kbd_key_backspace
Delete the last char.
ra8_err_t ra8_kbd_layout_init(ra8_kbd_layout_t *kb, const ra8_ui_rect_t *frame)
Lay the letters layer into frame; clear SHIFT.
char ra8_kbd_key_glyph(const ra8_kbd_layout_t *kb, uint8_t key_idx)
Effective character a char key emits given the live SHIFT state.
@ k_ra8_kbd_layer_symbols
Brackets / math symbols (#+=).
@ k_ra8_kbd_layer_numbers
Digits + common symbols (123).
Typed Port / Pin Constants for the RA8D2 IOPORT Module.
ra8_port_pin_t
Packed (port << 8) | pin pin identifier.
@ k_ra8_pin_none
Sentinel: no pin selected.
ra8_level_t
Digital output / input level.
@ k_ra8_level_high
Drive or read 1.
@ k_ra8_level_low
Drive or read 0.
High-level GPIO helpers on top of the PORT + PFS register layer.
ra8_err_t ra8_gpio_read(ra8_port_pin_t pin, ra8_level_t *out_level)
Read a previously-configured input.
Definition gpio.c:210
Multi-touch input driver – GoodIX GT911 backend.
ra8_err_t ra8_touch_read(ra8_touch_point_t *out_points, uint8_t max_count, uint8_t *got_count)
Drain the current touch frame.
Definition ra8_touch.c:551
Bounded UI interaction core: hit-testing, screen stack, paging.
ra8_err_t ra8_ui_nav_push(ra8_ui_nav_t *nav, uint16_t screen)
Push a new screen onto the stack.
Definition ra8_ui.c:79
ra8_err_t ra8_ui_nav_pop(ra8_ui_nav_t *nav, uint16_t *out_screen)
Pop the top screen, revealing the one beneath.
Definition ra8_ui.c:93
bool ra8_ui_rect_contains(const ra8_ui_rect_t *r, int32_t px, int32_t py)
Test whether a point lies inside a rectangle.
Definition ra8_ui.c:34
ra8_err_t ra8_ui_nav_top(const ra8_ui_nav_t *nav, uint16_t *out_screen)
Read the current (top) screen id.
Definition ra8_ui.c:115
ra8_err_t ra8_ui_hit_test(const ra8_ui_target_t *targets, uint16_t count, int32_t px, int32_t py, uint16_t *out_action, bool *out_hit)
Find the first tap target containing a point.
Definition ra8_ui.c:42
Zero-heap composable widget layer (dwm-style) over ra8_box + ra8_ui.
@ k_ra8_widget_axis_col
Vertical stack (top -> bottom).
Definition ra8_widget.h:176
ra8_err_t ra8_widget_layout_stack(ra8_widget_t *widgets, uint16_t count, const ra8_ui_rect_t *frame, ra8_widget_axis_t axis, int16_t gap, int16_t pad, ra8_box_t *box_scratch, uint16_t box_cap)
Lay a stack of widgets out inside a frame (delegates to ra8_box).
Definition ra8_widget.c:215
ra8_err_t ra8_widget_invalidate(ra8_widget_t *w, ra8_widget_refresh_t refresh)
Mark a widget dirty with a refresh hint (folds upward in strength).
Definition ra8_widget.c:286
@ k_ra8_widget_refresh_quality
Full / GC16 (whole-area redraw).
Definition ra8_widget.h:101
ra8_err_t ra8_widget_render_dirty(ra8_widget_t *widgets, uint16_t count)
Render every visible + dirty widget, clearing its damage.
Definition ra8_widget.c:331
One node in a box tree (caller-owned).
Definition ra8_box.h:90
One decoded touch contact.
Definition ra8_touch.h:93
uint16_t x
Panel X coordinate.
Definition ra8_touch.h:94
uint16_t y
Panel Y coordinate.
Definition ra8_touch.h:95
Axis-aligned rectangle in framebuffer pixel coordinates.
Definition ra8_ui.h:72
int32_t w
Width (pixels, >= 0).
Definition ra8_ui.h:75
int32_t h
Height (pixels, >= 0).
Definition ra8_ui.h:76
int32_t x
Left edge (pixels).
Definition ra8_ui.h:73
int32_t y
Top edge (pixels).
Definition ra8_ui.h:74
One widget instance (caller-owned, no allocation).
Behaviour table shared by all widgets of one kind.
Definition ra8_widget.h:121