ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
renderer.c File Reference

render_file_* — ties together font resolution, platform renderer, FFmpeg encoder More...

Go to the source code of this file.

Data Structures

struct  render_file_ctx_s
 For render file ctx, this is the live record that ties progress, error, renderer, encoder together. The owning code creates it, updates it while work is active, and releases its handles and pointed-to storage before freeing the record. More...

Macros

#define RENDER_AUTO_FONT_SIZE_PT   12.0
#define RENDER_AUTO_VIDEO_MAX_DIMENSION   1280
#define RENDER_AUTO_MIN_FONT_SIZE_PT   3.0

Functions

asciichat_error_t render_file_create (const char *output_path, int cols, int rows, int fps, int theme, render_file_ctx_t **out)
void render_file_set_live_timing (render_file_ctx_t *ctx)
asciichat_error_t render_file_write_frame (render_file_ctx_t *ctx, const char *frame, uint64_t captured_ns)
void render_file_begin_drain (render_file_ctx_t *ctx, uint64_t total, bool total_known)
void render_file_set_snapshot_actual_duration (render_file_ctx_t *ctx, double actual_duration_sec)
asciichat_error_t render_file_destroy (render_file_ctx_t *ctx)

Detailed Description

render_file_* — ties together font resolution, platform renderer, FFmpeg encoder

Definition in file renderer.c.

Macro Definition Documentation

◆ RENDER_AUTO_FONT_SIZE_PT

#define RENDER_AUTO_FONT_SIZE_PT   12.0

Definition at line 23 of file renderer.c.

Referenced by render_file_create().

◆ RENDER_AUTO_MIN_FONT_SIZE_PT

#define RENDER_AUTO_MIN_FONT_SIZE_PT   3.0

Definition at line 25 of file renderer.c.

Referenced by render_file_create().

◆ RENDER_AUTO_VIDEO_MAX_DIMENSION

#define RENDER_AUTO_VIDEO_MAX_DIMENSION   1280

Definition at line 24 of file renderer.c.

Referenced by render_file_create().

Function Documentation

◆ render_file_begin_drain()

void render_file_begin_drain ( render_file_ctx_t * ctx,
uint64_t total,
bool total_known )

Definition at line 364 of file renderer.c.

364 {
365 if (ctx)
366 render_progress_begin(ctx->progress, total, total_known);
367}
void render_progress_begin(render_progress_t *progress, uint64_t total, bool total_known)
render_progress_t * progress
Definition renderer.c:33

References render_file_ctx_s::progress, and render_progress_begin().

Referenced by session_display_begin_drain().

◆ render_file_create()

asciichat_error_t render_file_create ( const char * output_path,
int cols,
int rows,
int fps,
int theme,
render_file_ctx_t ** out )

Definition at line 53 of file renderer.c.

54 {
55 if (!output_path || !out || cols <= 0 || rows <= 0)
56 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing render output, grid, or context");
57 *out = NULL;
58 log_info("output_path=%s, cols=%d, rows=%d, fps=%d, theme=%d, out=%p", output_path ? output_path : "(null)", cols,
59 rows, fps, theme, (void *)out);
60 render_file_ctx_t *ctx = SAFE_CALLOC(1, sizeof(*ctx), render_file_ctx_t *);
61 if (strcmp(output_path, "/dev/null") == 0) {
62 ctx->discard = true;
63 ctx->error = ASCIICHAT_OK;
64 *out = ctx;
65 return ASCIICHAT_OK;
66 }
67 ctx->kitty_only = strcmp(output_path, "--kitty") == 0;
68 ctx->kitty = ctx->kitty_only || GET_OPTION(render_kitty);
69 if (ctx->kitty && !GET_OPTION(snapshot_mode)) {
70 SAFE_FREE(ctx);
71 return SET_ERRNO(ERROR_INVALID_PARAM, "Kitty graphics output requires --snapshot");
72 }
73 if (ctx->kitty && (strcmp(output_path, "-") == 0 || strcmp(output_path, "pipe:") == 0)) {
74 SAFE_FREE(ctx);
75 return SET_ERRNO(ERROR_INVALID_PARAM, "Kitty graphics and binary media cannot share stdout");
76 }
77 const char *term = SAFE_GETENV("TERM");
78 if (ctx->kitty && terminal_is_stdout_tty() &&
79 (!term || strcmp(term, "dumb") == 0 || strcmp(term, "linux") == 0 || strcmp(term, "vt100") == 0)) {
80 SAFE_FREE(ctx);
82 "Kitty graphics require a capable terminal; redirect stdout to export the protocol");
83 }
84
85 // Resolve font spec → platform-appropriate file path, family name, or bundled data.
86 char font_spec[PLATFORM_MAX_PATH_LENGTH] = {0};
87 bool font_is_path = false;
88 const uint8_t *font_data = NULL;
89 size_t font_data_size = 0;
90 const char *raw_font = GET_OPTION(render_font);
91
92 // Log the font option value from GET_OPTION (for WASM font debugging)
93 log_info("[WASM_FONT_GETTER] GET_OPTION(render_font) returned: '%s' (empty=%d), GET_OPTION(matrix_rain)=%d",
94 raw_font ? raw_font : "(null)", (!raw_font || raw_font[0] == '\0') ? 1 : 0, GET_OPTION(matrix_rain));
95
96 // Auto-select matrix font when --matrix flag is set, unless user explicitly overrides with --render-font
97 log_debug("raw_font=%s, matrix_rain=%d", raw_font, GET_OPTION(matrix_rain));
98 if ((!raw_font || raw_font[0] == '\0') && GET_OPTION(matrix_rain)) {
99 raw_font = "matrix";
100 log_debug("[MATRIX] Using matrix font (enabled by --matrix flag)");
101 }
102
103 platform_font_register("matrix", g_font_matrix_resurrected, g_font_matrix_resurrected_size);
104 platform_font_register("default", g_font_default, g_font_default_size);
105 platform_font_register("DejaVu Sans Mono", g_font_default, g_font_default_size);
107 platform_font_resolve(raw_font, font_spec, sizeof(font_spec), &font_is_path, &font_data, &font_data_size);
108 if (fe != ASCIICHAT_OK) {
109 if (raw_font && raw_font[0]) {
110 SAFE_FREE(ctx);
111 return fe;
112 }
113 log_warn("font resolution failed for '%s' — using bundled default", raw_font ? raw_font : "(explicit system)");
114 fe = platform_font_resolve("default", font_spec, sizeof(font_spec), &font_is_path, &font_data, &font_data_size);
115 if (fe != ASCIICHAT_OK) {
116 SAFE_FREE(ctx);
117 return fe;
118 }
119 }
120
121 log_debug("Font resolved: font_spec='%s', font_is_path=%d, font_data=%p (size=%zu)", font_spec, font_is_path,
122 (void *)font_data, font_data_size);
123
124 // A zero size selects a compact default for video; explicit font sizes keep
125 // their exact dimensions. Images and Kitty snapshots retain the 12 pt default.
126 double font_size_pt = GET_OPTION(render_font_size);
127 const char *extension = strrchr(output_path, '.');
128 bool is_image =
129 extension && (!strcmp(extension, ".png") || !strcmp(extension, ".jpg") || !strcmp(extension, ".jpeg") ||
130 !strcmp(extension, ".bmp") || !strcmp(extension, ".webp"));
131 bool automatic_video_size = font_size_pt == 0.0 && !is_image && !ctx->kitty;
132 if (font_size_pt == 0.0) {
133 font_size_pt = RENDER_AUTO_FONT_SIZE_PT;
134 if (automatic_video_size) {
135 double width_scale = (double)RENDER_AUTO_VIDEO_MAX_DIMENSION / ((double)cols * 10.0);
136 double height_scale = (double)RENDER_AUTO_VIDEO_MAX_DIMENSION / ((double)rows * 20.0);
137 double scale = width_scale < height_scale ? width_scale : height_scale;
138 if (scale < 1.0)
139 font_size_pt *= scale;
140 if (font_size_pt < RENDER_AUTO_MIN_FONT_SIZE_PT)
141 font_size_pt = RENDER_AUTO_MIN_FONT_SIZE_PT;
142 }
143 }
144 term_renderer_theme_t resolved_theme = (term_renderer_theme_t)theme;
145 if (resolved_theme == TERM_RENDERER_THEME_AUTO) {
146 resolved_theme =
148 log_debug("auto theme resolved to %s", resolved_theme == TERM_RENDERER_THEME_DARK ? "dark" : "light");
149 }
150
151 term_renderer_config_t tr_cfg = {
152 .cols = cols,
153 .rows = rows,
154 .font_size_pt = font_size_pt,
155 .theme = resolved_theme,
156 .font_is_path = font_is_path,
157 .font_data = font_data,
158 .font_data_size = font_data_size,
159 };
160 SAFE_STRNCPY(tr_cfg.font_spec, font_spec, sizeof(tr_cfg.font_spec));
161
162 asciichat_error_t err = term_renderer_create(&tr_cfg, &ctx->renderer);
163 if (err != ASCIICHAT_OK) {
164 log_error("term_renderer_create failed: %s", asciichat_error_format(NULL, err));
165 SAFE_FREE(ctx);
166 return err;
167 }
168 int actual_width_px = term_renderer_width_px(ctx->renderer);
169 int actual_height_px = term_renderer_height_px(ctx->renderer);
170 // Hinting and font metrics vary. Recalculate once if the initial estimate
171 // exceeds the target, retaining the original renderer if a bitmap font
172 // cannot be scaled down.
173 if (automatic_video_size &&
174 (actual_width_px > RENDER_AUTO_VIDEO_MAX_DIMENSION || actual_height_px > RENDER_AUTO_VIDEO_MAX_DIMENSION) &&
175 font_size_pt > RENDER_AUTO_MIN_FONT_SIZE_PT) {
176 double width_scale = (double)RENDER_AUTO_VIDEO_MAX_DIMENSION / actual_width_px;
177 double height_scale = (double)RENDER_AUTO_VIDEO_MAX_DIMENSION / actual_height_px;
178 double scale = width_scale < height_scale ? width_scale : height_scale;
179 double smaller = font_size_pt * scale * 0.98;
180 if (smaller < RENDER_AUTO_MIN_FONT_SIZE_PT)
182 tr_cfg.font_size_pt = smaller;
183 terminal_renderer_t *candidate = NULL;
184 err = term_renderer_create(&tr_cfg, &candidate);
185 if (err == ASCIICHAT_OK) {
186 int candidate_width = term_renderer_width_px(candidate);
187 int candidate_height = term_renderer_height_px(candidate);
188 if (candidate_width <= actual_width_px && candidate_height <= actual_height_px &&
189 (candidate_width < actual_width_px || candidate_height < actual_height_px)) {
191 ctx->renderer = candidate;
192 actual_width_px = candidate_width;
193 actual_height_px = candidate_height;
194 } else {
195 term_renderer_destroy(candidate);
196 }
197 } else {
199 SAFE_FREE(ctx);
200 return err;
201 }
202 }
203 log_info("GRID DIMENSIONS: %ux%u cells -> PIXEL DIMENSIONS: %dx%d px", cols, rows, actual_width_px, actual_height_px);
204 log_debug("term_renderer created (%dx%d cells, %dx%d px)", cols, rows, actual_width_px, actual_height_px);
205
206 if (ctx->kitty_only) {
207 ctx->fps = fps;
208 ctx->progress = render_progress_create(true, "terminal");
209 NAMED_REGISTER_CONTEXT(ctx, "render_file_ctx", output_path, NULL);
211 *out = ctx;
212 return ASCIICHAT_OK;
213 }
214 int output_width_px = actual_width_px;
215 int output_height_px = actual_height_px;
216 if (automatic_video_size &&
217 (output_width_px > RENDER_AUTO_VIDEO_MAX_DIMENSION || output_height_px > RENDER_AUTO_VIDEO_MAX_DIMENSION)) {
218 double scale_x = (double)RENDER_AUTO_VIDEO_MAX_DIMENSION / output_width_px;
219 double scale_y = (double)RENDER_AUTO_VIDEO_MAX_DIMENSION / output_height_px;
220 double scale = scale_x < scale_y ? scale_x : scale_y;
221 output_width_px = ((int)(output_width_px * scale)) & ~1;
222 output_height_px = ((int)(output_height_px * scale)) & ~1;
223 if (output_width_px < 2)
224 output_width_px = 2;
225 if (output_height_px < 2)
226 output_height_px = 2;
227 }
228 err = ffmpeg_encoder_create_scaled(output_path, actual_width_px, actual_height_px, output_width_px, output_height_px,
229 fps, &ctx->encoder);
230 if (err != ASCIICHAT_OK) {
231 log_error("ffmpeg_encoder_create failed: %s", asciichat_error_format(NULL, err));
233 SAFE_FREE(ctx);
234 return err;
235 }
236 log_debug("ffmpeg_encoder created successfully");
237
238 // Initialize audio fields
239 ctx->audio_sample_rate = 48000; // Audio pipeline is 48kHz
240 ctx->fps = fps;
241 // Allocate large buffer for snapshot mode (5 seconds at 48kHz with ~14 FPS = 3,428 samples per frame)
242 // Use 8192 to comfortably handle snapshot reads without excessive overhead
243 ctx->audio_buf_size = 8192; // Temporary buffer for reading audio (in floats)
244 ctx->audio_read_buf = SAFE_MALLOC(ctx->audio_buf_size * sizeof(float), float *);
245
247 if (err != ASCIICHAT_OK) {
251 SAFE_FREE(ctx);
252 return err;
253 }
254 log_info("initialized encoder for %s", output_path);
255
256 // Register render file context for debugging
257 NAMED_REGISTER_CONTEXT(ctx, "render_file_ctx", output_path, NULL);
258
259 bool binary_stdout = strcmp(output_path, "-") == 0 || strcmp(output_path, "pipe:") == 0;
260 ctx->progress = render_progress_create(!binary_stdout, output_path);
262 *out = ctx;
263 return ASCIICHAT_OK;
264}
const char * asciichat_error_format(const char *message, asciichat_error_t code)
#define SET_ERRNO(code,...)
terminal_background_t colorscheme_current_background(void)
@ TERM_BACKGROUND_DARK
Definition colorscheme.h:67
asciichat_error_t ffmpeg_encoder_create_scaled(const char *output_path, int source_width, int source_height, int width_px, int height_px, int fps, ffmpeg_encoder_t **out)
asciichat_error_t ffmpeg_encoder_destroy(ffmpeg_encoder_t *enc)
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:423
#define SAFE_FREE(ptr)
Definition common.h:377
#define SAFE_MALLOC(size, cast)
Definition common.h:265
#define SAFE_GETENV(name)
Definition common.h:449
#define SAFE_CALLOC(count, size, cast)
Definition common.h:275
#define PLATFORM_MAX_PATH_LENGTH
Definition common.h:95
unsigned char uint8_t
Definition common.h:60
#define NAMED_REGISTER_CONTEXT(context, context_type, name, parent_ptr)
Register a generic context with automatic format specifier.
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_INVALID_PARAM
@ ERROR_NOT_SUPPORTED
Definition error_codes.h:69
#define log_warn(...)
Log a WARN message.
Definition log/log.h:621
#define log_error(...)
Log an ERROR message.
Definition log/log.h:634
#define log_info(...)
Log an INFO message.
Definition log/log.h:608
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:595
#define GET_OPTION(field)
Safely get a specific option field (lock-free read).
Definition options.h:1336
const size_t g_font_matrix_resurrected_size
const unsigned char g_font_matrix_resurrected[]
const size_t g_font_default_size
const unsigned char g_font_default[]
int term_renderer_width_px(terminal_renderer_t *r)
asciichat_error_t term_renderer_create(const term_renderer_config_t *cfg, terminal_renderer_t **out)
void term_renderer_destroy(terminal_renderer_t *r)
int term_renderer_height_px(terminal_renderer_t *r)
static bool size_t gotel_query_t * out
Definition queries.h:8
asciichat_error_t audio_recording_create_channels(audio_recording_t **out, unsigned channels)
Definition recording.c:34
render_progress_t * render_progress_create(bool enabled, const char *destination)
#define RENDER_AUTO_VIDEO_MAX_DIMENSION
Definition renderer.c:24
#define RENDER_AUTO_FONT_SIZE_PT
Definition renderer.c:23
#define RENDER_AUTO_MIN_FONT_SIZE_PT
Definition renderer.c:25
struct terminal_renderer_s terminal_renderer_t
Definition renderer.h:18
term_renderer_theme_t
Terminal rendering theme.
Definition renderer.h:27
@ TERM_RENDERER_THEME_LIGHT
Definition renderer.h:29
@ TERM_RENDERER_THEME_DARK
Definition renderer.h:28
@ TERM_RENDERER_THEME_AUTO
Definition renderer.h:30
struct render_file_ctx_s render_file_ctx_t
Definition renderer.h:63
asciichat_error_t error
Definition renderer.c:34
terminal_renderer_t * renderer
Definition renderer.c:35
ffmpeg_encoder_t * encoder
Definition renderer.c:36
uint32_t audio_sample_rate
Definition renderer.c:47
audio_recording_t * recording
Definition renderer.c:37
float * audio_read_buf
Definition renderer.c:49
char font_spec[PLATFORM_MAX_PATH_LENGTH]
Definition renderer.h:38
void ui_controller_title_render(ui_title_render_t phase)
@ UI_TITLE_RENDERING

References asciichat_error_format(), ASCIICHAT_OK, render_file_ctx_s::audio_buf_size, render_file_ctx_s::audio_read_buf, audio_recording_create_channels(), render_file_ctx_s::audio_sample_rate, colorscheme_current_background(), render_file_ctx_s::discard, render_file_ctx_s::encoder, render_file_ctx_s::error, ERROR_INVALID_PARAM, ERROR_NOT_SUPPORTED, ffmpeg_encoder_create_scaled(), ffmpeg_encoder_destroy(), term_renderer_config_t::font_size_pt, term_renderer_config_t::font_spec, render_file_ctx_s::fps, g_font_default, g_font_default_size, g_font_matrix_resurrected, g_font_matrix_resurrected_size, GET_OPTION, render_file_ctx_s::kitty, render_file_ctx_s::kitty_only, log_debug, log_error, log_info, log_warn, NAMED_REGISTER_CONTEXT, out, PLATFORM_MAX_PATH_LENGTH, render_file_ctx_s::progress, render_file_ctx_s::recording, RENDER_AUTO_FONT_SIZE_PT, RENDER_AUTO_MIN_FONT_SIZE_PT, RENDER_AUTO_VIDEO_MAX_DIMENSION, render_progress_create(), render_file_ctx_s::renderer, SAFE_CALLOC, SAFE_FREE, SAFE_GETENV, SAFE_MALLOC, SAFE_STRNCPY, SET_ERRNO, TERM_BACKGROUND_DARK, term_renderer_create(), term_renderer_destroy(), term_renderer_height_px(), TERM_RENDERER_THEME_AUTO, TERM_RENDERER_THEME_DARK, TERM_RENDERER_THEME_LIGHT, term_renderer_width_px(), ui_controller_title_render(), and UI_TITLE_RENDERING.

Referenced by session_display_init_render_file().

◆ render_file_destroy()

asciichat_error_t render_file_destroy ( render_file_ctx_t * ctx)

Definition at line 375 of file renderer.c.

375 {
376 if (!ctx)
377 return ASCIICHAT_OK;
378 if (ctx->discard) {
379 SAFE_FREE(ctx);
380 return ASCIICHAT_OK;
381 }
382 NAMED_UNREGISTER(ctx);
385 uint64_t target = ctx->audio_target;
387 while (ctx->audio_samples_written < target) {
388 int count = (int)((target - ctx->audio_samples_written) > (uint64_t)ctx->audio_buf_size
390 : target - ctx->audio_samples_written);
392 err = ffmpeg_encoder_write_audio(ctx->encoder, ctx->audio_read_buf, count);
393 if (err != ASCIICHAT_OK)
394 break;
395 ctx->audio_samples_written += count;
396 }
399 if (err == ASCIICHAT_OK)
400 err = close_err;
401 if (ctx->error != ASCIICHAT_OK)
402 err = ctx->error;
404 if (err == ASCIICHAT_OK && ctx->kitty && ctx->frames_written == 0)
405 err = SET_ERRNO(ERROR_MEDIA_INIT, "No snapshot was rendered for Kitty output");
406 if (err == ASCIICHAT_OK && ctx->kitty && ctx->frames_written > 0)
412 SAFE_FREE(ctx);
413 return err;
414}
asciichat_error_t ffmpeg_encoder_write_audio(ffmpeg_encoder_t *enc, const float *samples, int num_samples)
unsigned long long uint64_t
Definition common.h:63
#define NAMED_UNREGISTER(ptr)
Unregister a pointer.
@ ERROR_MEDIA_INIT
Definition error_codes.h:65
asciichat_error_t kitty_image_write(FILE *stream, const uint8_t *rgba, int width, int height, int pitch)
const uint8_t * term_renderer_pixels(terminal_renderer_t *r)
int term_renderer_pitch(terminal_renderer_t *r)
void audio_recording_read(audio_recording_t *recording, float *samples, size_t count)
Definition recording.c:127
void audio_recording_destroy(audio_recording_t *recording)
Definition recording.c:54
void render_progress_finalize(render_progress_t *progress)
void render_progress_destroy(render_progress_t *progress)
uint64_t audio_samples_written
Definition renderer.c:39
uint64_t frames_written
Definition renderer.c:38
uint64_t audio_target
Definition renderer.c:42
@ UI_TITLE_FINALIZING
@ UI_TITLE_FAILED
@ UI_TITLE_IDLE

References ASCIICHAT_OK, render_file_ctx_s::audio_buf_size, render_file_ctx_s::audio_read_buf, audio_recording_destroy(), audio_recording_read(), render_file_ctx_s::audio_samples_written, render_file_ctx_s::audio_target, render_file_ctx_s::discard, render_file_ctx_s::encoder, render_file_ctx_s::error, ERROR_MEDIA_INIT, ffmpeg_encoder_destroy(), ffmpeg_encoder_write_audio(), render_file_ctx_s::frames_written, render_file_ctx_s::kitty, kitty_image_write(), NAMED_UNREGISTER, render_file_ctx_s::progress, render_file_ctx_s::recording, render_progress_destroy(), render_progress_finalize(), render_file_ctx_s::renderer, SAFE_FREE, SET_ERRNO, term_renderer_destroy(), term_renderer_height_px(), term_renderer_pitch(), term_renderer_pixels(), term_renderer_width_px(), ui_controller_title_render(), UI_TITLE_FAILED, UI_TITLE_FINALIZING, and UI_TITLE_IDLE.

Referenced by session_display_finish_render_file().

◆ render_file_set_live_timing()

void render_file_set_live_timing ( render_file_ctx_t * ctx)

Definition at line 266 of file renderer.c.

266 {
267 if (ctx) {
268 ctx->live_timing = true;
269 if (ctx->encoder)
271 }
272}
void ffmpeg_encoder_set_live_timing(ffmpeg_encoder_t *enc)

References render_file_ctx_s::encoder, ffmpeg_encoder_set_live_timing(), and render_file_ctx_s::live_timing.

Referenced by session_display_render_frame(), and session_display_set_render_live_timing().

◆ render_file_set_snapshot_actual_duration()

void render_file_set_snapshot_actual_duration ( render_file_ctx_t * ctx,
double actual_duration_sec )

Definition at line 369 of file renderer.c.

369 {
370 if (!ctx || !ctx->encoder)
371 return;
372 ffmpeg_encoder_set_snapshot_actual_duration(ctx->encoder, actual_duration_sec);
373}
void ffmpeg_encoder_set_snapshot_actual_duration(ffmpeg_encoder_t *enc, double actual_duration_sec)

References render_file_ctx_s::encoder, and ffmpeg_encoder_set_snapshot_actual_duration().

Referenced by session_display_set_snapshot_actual_duration().

◆ render_file_write_frame()

asciichat_error_t render_file_write_frame ( render_file_ctx_t * ctx,
const char * frame,
uint64_t captured_ns )

Definition at line 348 of file renderer.c.

348 {
349 if (!ctx || !frame)
350 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing render context or frame");
351 if (ctx->error != ASCIICHAT_OK)
352 return ctx->error;
353 if (ctx->discard)
354 return ASCIICHAT_OK;
355 asciichat_error_t err = render_file_write_frame_internal(ctx, frame, captured_ns);
356 if (err != ASCIICHAT_OK)
358 render_progress_frame(ctx->progress, frame, err == ASCIICHAT_OK);
359 if (err != ASCIICHAT_OK)
360 ctx->error = err;
361 return err;
362}
void render_progress_frame(render_progress_t *progress, const char *frame, bool succeeded)

References ASCIICHAT_OK, render_file_ctx_s::discard, render_file_ctx_s::error, ERROR_INVALID_PARAM, render_file_ctx_s::progress, render_progress_frame(), SET_ERRNO, ui_controller_title_render(), and UI_TITLE_FAILED.

Referenced by session_display_encode_frame(), and session_display_render_frame().