ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
emu_presentation.c
Go to the documentation of this file.
1
12
13#include "emu_presentation.h"
14
15#include <errno.h>
16#include <fcntl.h>
17#include <stdalign.h>
18#include <stdlib.h>
19#include <string.h>
20#include <unistd.h>
21
23#include "emu_view.h"
24
29
42RA8_INTERNAL static int internal_anonymous_fd(size_t size, int* os_error)
43{
44 char temporary[] = "/tmp/ra8-emu-present.XXXXXX";
45 const int fd = mkstemp(temporary);
46 if (fd < 0) {
47 *os_error = errno;
48 return -1;
49 }
50 const int unlink_result = unlink(temporary);
51 const int unlink_error = errno;
52 if ((unlink_result != 0) || (ftruncate(fd, (off_t)size) != 0)) {
53 *os_error = (unlink_result != 0) ? unlink_error : errno;
54 (void)close(fd);
55 if (unlink_result != 0) {
56 (void)unlink(temporary);
57 }
58 return -1;
59 }
60 return fd;
61}
62
74
91 size_t surface,
92 size_t required_scratch,
93 size_t supplied_scratch,
94 int os_error)
95{
96 return (emu_presentation_result_t){.status = status,
97 .required_surface_bytes = surface,
98 .required_scratch_bytes = required_scratch,
99 .supplied_scratch_bytes = supplied_scratch,
100 .os_error = os_error};
101}
102
116RA8_INTERNAL static bool internal_add(size_t left, size_t right, size_t* result)
117{
118 if ((result == nullptr) || (right > (SIZE_MAX - left))) {
119 return false;
120 }
121 *result = left + right;
122 return true;
123}
124
138RA8_INTERNAL static bool internal_multiply(size_t left, size_t right, size_t* result)
139{
140 if ((result == nullptr) || ((left != 0U) && (right > (SIZE_MAX / left)))) {
141 return false;
142 }
143 *result = left * right;
144 return true;
145}
146
158RA8_INTERNAL static bool internal_rotation_valid(uint32_t rotation)
159{
160 return (rotation == (uint32_t)k_rotate_0) || (rotation == (uint32_t)k_rotate_90) ||
161 (rotation == (uint32_t)k_rotate_180) || (rotation == (uint32_t)k_rotate_270);
162}
163
178{
179 if ((spec == nullptr) || (plan == nullptr) || (spec->panel_width == 0U) ||
180 (spec->panel_height == 0U) || !internal_rotation_valid(spec->rotate_deg)) {
182 }
183 const bool swap =
184 (spec->rotate_deg == (uint32_t)k_rotate_90) || (spec->rotate_deg == (uint32_t)k_rotate_270);
185 const size_t display_width = swap ? spec->panel_height : spec->panel_width;
186 const size_t display_height = swap ? spec->panel_width : spec->panel_height;
187 size_t composite_width = 0U;
188 if (!internal_add(display_width, (size_t)k_board_overlay_sidebar_width_px, &composite_width)) {
190 }
191 const size_t composite_height = (display_height > (size_t)k_board_overlay_min_height_px)
192 ? display_height
194 size_t composite_pixels = 0U;
195 size_t surface_bytes = 0U;
196 if (!internal_multiply(composite_width, composite_height, &composite_pixels) ||
197 !internal_multiply(composite_pixels, sizeof(uint16_t), &surface_bytes)) {
199 }
200 const size_t tile_width = (spec->panel_width < k_emu_presentation_tile_px)
201 ? spec->panel_width
203 const size_t tile_height = (spec->panel_height < k_emu_presentation_tile_px)
204 ? spec->panel_height
206 size_t tile_pixels = 0U;
207 size_t tile_bytes = 0U;
208 size_t row_bytes = 0U;
209 if (!internal_multiply(tile_width, tile_height, &tile_pixels) ||
210 !internal_multiply(tile_pixels, sizeof(uint16_t), &tile_bytes) ||
211 !internal_multiply(composite_width, sizeof(uint16_t), &row_bytes)) {
213 }
214 size_t rotation_bytes = tile_bytes;
215 if ((spec->rotate_deg != 0U) && !internal_multiply(rotation_bytes, 2U, &rotation_bytes)) {
217 }
218 const size_t scratch_bytes = (row_bytes > rotation_bytes) ? row_bytes : rotation_bytes;
220 (spec->panel_height > k_emu_presentation_max_panel_px) || (composite_width > UINT16_MAX) ||
221 (composite_height > UINT16_MAX)) {
223 }
224 *plan = (emu_presentation_plan_t){.panel_width = spec->panel_width,
225 .panel_height = spec->panel_height,
226 .display_width = display_width,
227 .display_height = display_height,
228 .composite_width = composite_width,
229 .composite_height = composite_height,
230 .surface_bytes = spec->active ? surface_bytes : 0U,
231 .scratch_bytes = spec->active ? scratch_bytes : 0U};
233}
234
236{
237 emu_presentation_plan_t plan = {};
238 const emu_presentation_status_t status = internal_plan(spec, &plan);
239 return internal_result(status,
240 (status == k_emu_presentation_ok) ? plan.surface_bytes : 0U,
241 (status == k_emu_presentation_ok) ? plan.scratch_bytes : 0U,
242 0U,
243 0);
244}
245
247 void* scratch,
248 size_t supplied_scratch_bytes,
250{
251 emu_presentation_plan_t plan = {};
252 emu_presentation_status_t status = internal_plan(spec, &plan);
253 if ((status == k_emu_presentation_ok) && (plan.scratch_bytes > supplied_scratch_bytes)) {
255 }
256 if ((status == k_emu_presentation_ok) && (plan.scratch_bytes > 0U) && (scratch == nullptr)) {
258 }
259 if ((status == k_emu_presentation_ok) && (plan.scratch_bytes > 0U) &&
260 (((uintptr_t)scratch % alignof(uint16_t)) != 0U)) {
262 }
263 if ((status != k_emu_presentation_ok) || (workspace == nullptr)) {
264 return internal_result((workspace == nullptr) ? k_emu_presentation_invalid : status,
265 plan.surface_bytes,
266 plan.scratch_bytes,
267 supplied_scratch_bytes,
268 0);
269 }
270 int fd = -1;
271 if (plan.surface_bytes > 0U) {
272 int os_error = 0;
273 fd = internal_anonymous_fd(plan.surface_bytes, &os_error);
274 if (fd < 0) {
276 plan.surface_bytes,
277 plan.scratch_bytes,
278 supplied_scratch_bytes,
279 os_error);
280 }
281 }
282 const emu_presentation_workspace_t ready = {
283 .fd = fd,
284 .panel_width = (uint16_t)plan.panel_width,
285 .panel_height = (uint16_t)plan.panel_height,
286 .display_width = (uint16_t)plan.display_width,
287 .display_height = (uint16_t)plan.display_height,
288 .composite_width = (uint16_t)plan.composite_width,
289 .composite_height = (uint16_t)plan.composite_height,
290 .rotate_deg = spec->rotate_deg,
291 .scratch = (uint16_t*)scratch,
292 .scratch_bytes = plan.scratch_bytes,
293 .surface_bytes = plan.surface_bytes,
294 };
295 *workspace = ready;
297 plan.surface_bytes,
298 plan.scratch_bytes,
299 supplied_scratch_bytes,
300 0);
301}
302
304{
305 if (workspace == nullptr) {
306 return false;
307 }
308 const int fd = workspace->fd;
309 *workspace = (emu_presentation_workspace_t){.fd = -1};
310 return (fd < 0) || (close(fd) == 0);
311}
312
314 size_t offset,
315 void* bytes,
316 size_t count)
317{
318 if ((workspace == nullptr) || (workspace->fd < 0) || (bytes == nullptr) ||
319 (offset > workspace->surface_bytes) || (count > (workspace->surface_bytes - offset)) ||
320 (offset > (size_t)INT64_MAX)) {
321 return false;
322 }
323 return priv_emu_io_pread_exact(workspace->fd, bytes, count, (off_t)offset).status == k_emu_io_ok;
324}
325
327 size_t offset,
328 const void* bytes,
329 size_t count)
330{
331 if ((workspace == nullptr) || (workspace->fd < 0) || (bytes == nullptr) ||
332 (offset > workspace->surface_bytes) || (count > (workspace->surface_bytes - offset)) ||
333 (offset > (size_t)INT64_MAX)) {
334 return false;
335 }
336 return priv_emu_io_pwrite_exact(workspace->fd, bytes, count, (off_t)offset).status == k_emu_io_ok;
337}
338
339bool emu_presentation_fill(void* context,
340 uint16_t x,
341 uint16_t y,
342 uint16_t width,
343 uint16_t height,
344 uint16_t color)
345{
347 if ((workspace == nullptr) || (width == 0U) || (height == 0U) ||
348 ((size_t)x + width > workspace->composite_width) ||
349 ((size_t)y + height > workspace->composite_height)) {
350 return false;
351 }
352 const size_t row_bytes = (size_t)width * sizeof(uint16_t);
353 if (row_bytes > workspace->scratch_bytes) {
354 return false;
355 }
356 /* One row of RGB565 pixels in the borrowed scratch. Alignment to
357 * alignof(uint16_t) is a checked precondition of emu_presentation_open(). */
358 uint16_t* const row = workspace->scratch;
359 /* The borrowed scratch is documented "or nullptr"; this is the one place the
360 * fill path needs it, so the check lives here rather than in the guard above. */
361 if (row == nullptr) {
362 return false;
363 }
364 for (uint16_t column = 0U; column < width; column++) {
365 row[column] = color;
366 }
367 for (uint16_t delta = 0U; delta < height; delta++) {
368 const size_t pixel_offset =
369 ((size_t)(y + delta) * (size_t)workspace->composite_width) + (size_t)x;
370 if (!emu_presentation_write(workspace, pixel_offset * sizeof(uint16_t), row, row_bytes)) {
371 return false;
372 }
373 }
374 return true;
375}
376
378{
379 if ((workspace == nullptr) || (snapshot_fd == nullptr) || (workspace->fd < 0) ||
380 (workspace->scratch == nullptr) || (workspace->scratch_bytes == 0U)) {
381 return false;
382 }
383 int os_error = 0;
384 const int fd = internal_anonymous_fd(workspace->surface_bytes, &os_error);
385 (void)os_error;
386 if (fd < 0) {
387 return false;
388 }
389 bool ok = true;
390 for (size_t offset = 0U; ok && (offset < workspace->surface_bytes);) {
391 const size_t count = ((workspace->surface_bytes - offset) < workspace->scratch_bytes)
392 ? (workspace->surface_bytes - offset)
393 : workspace->scratch_bytes;
394 ok = emu_presentation_read(workspace, offset, workspace->scratch, count) &&
395 (priv_emu_io_pwrite_exact(fd, workspace->scratch, count, (off_t)offset).status ==
397 offset += count;
398 }
399 if (!ok) {
400 (void)close(fd);
401 return false;
402 }
403 *snapshot_fd = fd;
404 return true;
405}
406
407static_assert((size_t)k_emu_presentation_max_scratch_bytes ==
409 "maximum presentation scratch budget changed");
@ k_board_overlay_min_height_px
Minimum composite height.
@ k_board_overlay_sidebar_width_px
Pixels added right of panel.
Bounded raw-descriptor I/O seam for the RA8 emulator.
@ k_emu_io_ok
The complete operation succeeded.
emu_io_result_t priv_emu_io_pread_exact(int fd, void *buf, size_t count, off_t offset)
Read exactly count bytes at offset without changing the cursor.
emu_io_result_t priv_emu_io_pwrite_exact(int fd, const void *buf, size_t count, off_t offset)
Write exactly count bytes at offset without changing the cursor.
emu_presentation_result_t emu_presentation_requirements(const emu_presentation_spec_t *spec)
Compute exact raw-fd and scratch requirements without mutation.
bool emu_presentation_read(const emu_presentation_workspace_t *workspace, size_t offset, void *bytes, size_t count)
Read exact RGB565 bytes at a checked surface offset.
bool emu_presentation_write(emu_presentation_workspace_t *workspace, size_t offset, const void *bytes, size_t count)
Write exact RGB565 bytes at a checked surface offset.
static RA8_INTERNAL bool internal_multiply(size_t left, size_t right, size_t *result)
Checked size multiplication.
static RA8_INTERNAL emu_presentation_result_t internal_result(emu_presentation_status_t status, size_t surface, size_t required_scratch, size_t supplied_scratch, int os_error)
Construct a complete operation result.
static RA8_INTERNAL emu_presentation_status_t internal_plan(const emu_presentation_spec_t *spec, emu_presentation_plan_t *plan)
Compute all checked geometry and storage values.
bool emu_presentation_close(emu_presentation_workspace_t *workspace)
Close one owned presentation descriptor and invalidate the workspace.
static RA8_INTERNAL int internal_anonymous_fd(size_t size, int *os_error)
Create, unlink, and exactly size one anonymous temporary descriptor.
bool emu_presentation_snapshot(emu_presentation_workspace_t *workspace, int *snapshot_fd)
Create an immutable unlinked descriptor snapshot of one surface.
emu_presentation_result_t emu_presentation_open(const emu_presentation_spec_t *spec, void *scratch, size_t supplied_scratch_bytes, emu_presentation_workspace_t *workspace)
Create and bind one unlinked raw-fd presentation workspace.
presentation_invariant_t
Compile-time invariant values for presentation storage.
@ k_presentation_expected_scratch_bytes
Two maximum RGB565 tiles.
static RA8_INTERNAL bool internal_add(size_t left, size_t right, size_t *result)
Checked size addition.
bool emu_presentation_fill(void *context, uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t color)
Fill a checked RGB565 rectangle in a raw-fd surface.
static RA8_INTERNAL bool internal_rotation_valid(uint32_t rotation)
True for one supported clockwise rotation.
Bounded raw-descriptor presentation surface and scratch workspace.
emu_presentation_status_t
Presentation planning, creation, and transfer status.
@ k_emu_presentation_capacity
Caller scratch is smaller than required.
@ k_emu_presentation_overflow
A checked size/offset operation overflowed.
@ k_emu_presentation_invalid
Geometry, rotation, pointer, or fd invalid.
@ k_emu_presentation_ok
Operation completed exactly.
@ k_emu_presentation_io
Raw descriptor create/size/I/O failed.
@ k_emu_presentation_max_panel_px
Largest panel dimension.
@ k_emu_presentation_tile_px
Square rotation tile side.
@ k_emu_presentation_max_scratch_bytes
Exact maximum caller scratch: source plus rotated 64x64 RGB565 tiles.
Board-view presentation: frames, composition, input routing, panels.
@ k_rotate_0
Native orientation.
Definition emu_view.h:78
@ k_rotate_90
90 deg clockwise (-> portrait).
Definition emu_view.h:79
@ k_rotate_270
90 deg counter-clockwise.
Definition emu_view.h:81
@ k_rotate_180
Upside down.
Definition emu_view.h:80
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
emu_io_status_t status
Semantic completion status.
Complete internal geometry and storage plan.
size_t panel_height
Native height.
size_t composite_width
Display plus sidebar.
size_t display_width
Rotation-adjusted width.
size_t composite_height
Display/sidebar maximum.
size_t panel_width
Native width.
size_t scratch_bytes
Exact tile/row scratch length.
size_t surface_bytes
Exact raw RGB565 surface length.
size_t display_height
Rotation-adjusted height.
Exact result of a requirements or workspace operation.
Inputs controlling exact presentation requirements.
size_t panel_height
Native firmware panel height.
size_t panel_width
Native firmware panel width.
uint32_t rotate_deg
Display rotation: 0, 90, 180, or 270 degrees.
bool active
A view, snapshot, record, or click needs pixels.
One independent owned surface plus non-owning bounded scratch.
uint16_t composite_width
Display plus sidebar width.
int fd
Owned unlinked descriptor, or -1.
uint16_t composite_height
Display/sidebar maximum height.
uint16_t * scratch
Borrowed RGB565 scratch, or nullptr.
size_t surface_bytes
Exact raw-fd RGB565 length.
size_t scratch_bytes
Exact bound scratch prefix.